Skip to content

[P2.29] 11 CLI commands print a bogus 'Error: 1' line after every intentional error exit #1113

Description

@frankbria

Found by the #614 cold-start walkthrough on a clean container. Priority P2.29 — severity medium, category ux.

Problem

11 CLI commands print a bogus second error line, Error: 1, after every clean, intentional error exit. Observed on the quickstart path:

$ cf tasks generate
Error: No PRD found.
Add one first: codeframe prd add <file.md>
Error: 1

The Error: 1 is not an error — it is the exit code, stringified and printed as if it were a message. To a new user it reads as a second, unexplained failure right after a message that was otherwise clear and actionable.

Root cause

typer.Exit inherits from RuntimeError, so a deliberate raise typer.Exit(1) is caught by the command's own catch-all and re-printed:

# codeframe/cli/app.py — tasks_generate
if not latest_prd:
    console.print("[red]Error:[/red] No PRD found.")
    console.print("Add one first: codeframe prd add <file.md>")
    raise typer.Exit(1)          # line 64 — intentional
...
except Exception as e:            # line 172 — catches the Exit above
    console.print(f"[red]Error:[/red] {e}")   # prints "Error: 1"
    raise typer.Exit(1)

prd_generate already has the correct guard and does not exhibit this:

except typer.Exit:
    raise
except Exception as e:
    ...

So the fix is established in the codebase; it is just missing from the other commands.

Affected commands

import, prd add, prd list, prd stress-test, tasks generate, tasks tree, tasks list, tasks show, schedule predict, schedule bottlenecks, patch apply — every command that has a broad except Exception as e and also raises typer.Exit(...) without an except typer.Exit: raise ahead of it.

Acceptance criteria

  • All 11 commands re-raise typer.Exit before the catch-all, so an intentional exit prints no extra line
  • cf tasks generate with no PRD prints its two-line message and nothing else
  • A test covers at least cf tasks generate (no PRD) asserting Error: 1 is absent and the exit code is still 1
  • Preferably a shared helper or a lint rule, so a new command cannot reintroduce the pattern

Evidence

  • codeframe/cli/app.py:64 (intentional exit) and codeframe/cli/app.py:172 (catch-all that swallows it)
  • codeframe/cli/app.py:1693 — the correct guard, already present in prd_generate
  • Cold-start transcript: scripts/quickstart-cleanroom/artifacts-pypi-0.9.1/transcript.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    type:bugSomething is broken and needs fixingux

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions