Skip to content

Missing-dependency panel prints an uncopyable install command: over-escaped closing bracket #69

Description

@leesaenz

What happens

The DependencyMissingError panel prints an install command that cannot be
copied and run. Running phantom separate without the extra installed:

╭───────────────────────────── Missing Dependency ─────────────────────────────╮
│ phantom-audio-separation is not installed.                                   │
│                                                                              │
│ Install with: uv tool install "phantom-audio[separation\]" --python 3.13     │
╰──────────────────────────────────────────────────────────────────────────────╯

Note the stray backslash before the closing bracket: [separation\].

Cause

src/phantom/cli/_formatting.py:234:

f'Install with: [green]uv tool install "phantom-audio\\[{exc.extra}\\]" --python {RECOMMENDED_PYTHON}[/green]'

Rich markup only treats [ as a tag opener, so \[ is the documented escape for
a literal opening bracket and renders correctly. ] is not special and has no
escape sequence — Rich passes \] through unchanged, so the backslash reaches
the terminal.

The opening bracket is escaped correctly; the closing one is over-escaped.

Fix

-f'Install with: [green]uv tool install "phantom-audio\\[{exc.extra}\\]" --python {RECOMMENDED_PYTHON}[/green]'
+f'Install with: [green]uv tool install "phantom-audio\\[{exc.extra}]" --python {RECOMMENDED_PYTHON}[/green]'

Impact

Cosmetic, but it lands in front of a user who is already blocked and reaching for
the copy button. In zsh the pasted command fails outright:

$ uv tool install "phantom-audio[separation\]" --python 3.13
error: Expected an alphanumeric character while parsing extra name

Notes

render.py:111 and render.py:226 also raise DependencyMissingError panels
through the same renderer, so every missing-extra message carries the same stray
backslash — not just separate.

A test asserting the rendered panel text contains [separation] and not
[separation\] would catch it. Asserting on the markup string rather than the
rendered output would not, since the bug is in how Rich interprets the escape.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions