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
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
Problem
11 CLI commands print a bogus second error line,
Error: 1, after every clean, intentional error exit. Observed on the quickstart path:The
Error: 1is 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.Exitinherits fromRuntimeError, so a deliberateraise typer.Exit(1)is caught by the command's own catch-all and re-printed:prd_generatealready has the correct guard and does not exhibit this: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 broadexcept Exception as eand also raisestyper.Exit(...)without anexcept typer.Exit: raiseahead of it.Acceptance criteria
typer.Exitbefore the catch-all, so an intentional exit prints no extra linecf tasks generatewith no PRD prints its two-line message and nothing elsecf tasks generate(no PRD) assertingError: 1is absent and the exit code is still 1Evidence
codeframe/cli/app.py:64(intentional exit) andcodeframe/cli/app.py:172(catch-all that swallows it)codeframe/cli/app.py:1693— the correct guard, already present inprd_generatescripts/quickstart-cleanroom/artifacts-pypi-0.9.1/transcript.txt