Repository navigation
fix(cli): setup --scope user --plugins throws an uncaught UserScopePluginModeError instead of rendering a validation error #891
Description
Description
aidd setup --scope user --plugins <anything> dies with an uncaught exception, a full stack trace, and a dump of the minified bundle line it was thrown from:
UserScopePluginModeError: --scope user has no manifest entry a plugin can be recorded
against yet, so --plugins has nothing to enable. Drop --plugins, or run `aidd plugin install`
separately at project scope.
at SetupFlow.validateScope (…/cli/dist/cli.js:220:11360)
at new SetupFlow (…/cli/dist/cli.js:220:10333)
The message itself is good — it names the cause and the two ways out. It is simply thrown rather than rendered. A person exploring the feature reads a crash where the code already knows how to say "this combination is not supported yet".
The contrast is one command away: the same restriction, reached through plugin install, refuses cleanly. Both messages live in the same file (cli/src/kernel/errors.ts); one reaches the user as a sentence, the other as a stack trace.
Affected file(s)
cli/src/contexts/framework/domain/setup-flow.ts— throwsUserScopePluginModeError, from the constructor by way ofvalidateScope(which is a plausible reason it escapes the command's error boundary, though I have not read that boundary — take it as a hint, not a diagnosis).cli/src/kernel/errors.ts— holds both this message and the clean one quoted below.
Steps to reproduce
- From a project with an AIDD manifest, run
aidd setup --scope user --plugins <any plugin>. - Read the stack trace.
- For the contrast, run
aidd plugin install --scope user --tool claude.
Expected behaviour
setup renders the refusal the way plugin install already renders its own — a clear message, a non-zero exit code, no stack trace and no bundle dump:
Error: Tool 'claude' does not support scope 'user'. Supported scope: 'project'. Re-run with --scope project or omit the flag.
And, secondarily, that the real installScope: "user" support matrix is documented, or linked from setup --scope user's help text — so that a half-delivered state is discoverable by reading rather than by crashing.
Observed behaviour
Re-measured from scratch on 2026-09-20: next cloned at 83b0246e (unchanged since the first measurement), CLI 5.3.0 built from source, Node 26.5.0, pnpm 12.3.4, a fresh isolated HOME per tool, an empty git repository as the project.
The crash row, all four tools, exit code 1:
UserScopePluginModeError: --scope user has no manifest entry a plugin can be recorded against yet, so --plugins has nothing to enable. Drop --plugins, or run `aidd plugin install` separately at project scope.
at SetupFlow.validateScope (…/cli/dist/cli.js:220:11360)
at new SetupFlow (…/cli/dist/cli.js:220:10333)
at Command.<anonymous> (…/cli/dist/cli.js:220:16306)
at Command.listener [as _actionHandler] (…/commander/lib/command.js:569:17)
…
Node.js v26.5.0
One invocation prints 44,338 bytes over 13 lines, one of which is 41,770 characters of the minified bundle. The third frame — Command.<anonymous> — is the command's own action callback, which is where it leaves the CLI.
The rest of the row, per tool:
| claude | codex | copilot | cursor | |
|---|---|---|---|---|
setup --scope user (no --plugins) |
exit 0 | exit 0 | exit 0 | exit 0 |
~/.config/aidd/{manifest,marketplaces}.json written |
yes | yes | yes | yes |
built source under ~/.config/aidd/cache/built/ |
yes | yes | no | no |
| marketplace registered in the host's own config | yes — ~/.claude/settings.json, ~/.claude/plugins/known_marketplaces.json |
yes — [marketplaces.aidd-framework] in ~/.codex/config.toml |
no | no |
| nothing written under the project | yes | yes | yes | yes |
plugin install --scope user --tool <t> |
refused, clean message | refused, clean | refused, clean | see below |
setup --scope user --plugins … |
uncaught crash | uncaught crash | uncaught crash | uncaught crash |
Two cells I reported more confidently than the evidence allowed, corrected here:
- The built source and host registration are not properties of the scope; they follow whether the host's CLI is on PATH. On this machine
claudeandcodexare,copilotandcursorare not. Copilot says so — "Warning: copilot CLI not found on PATH — skipping native plugin activation" — and still exits 0. Cursor exits 0 writing nothing machine-wide and printing no such warning; I have not looked into whether its activation is CLI-based at all, so that row is an observation, not a claim about Cursor. - The
cursorcell of theplugin installrow is environment-dependent. It answered "No AIDD manifest found" in my first run and "Plugin 'aidd-dev' was not found in any registered marketplace" today, from a different starting state. Either way it does not reach the scope refusal, which is the only thing this row is evidence about.
So the first half of a user-scope install exists and works; the second half refuses properly through one entry point and crashes through the other.
AI tool
Other — the crash is in the aidd CLI itself, before any host is touched; the row above is the --tools value passed, not the tool driving the session.
aidd-cli version
5.3.0, built from source at next 83b0246e, Node 26.5.0.
OS
macOS.
Additional context
Found during the #859 spike, kept out of that thread because it is not about Kimi/Antigravity telemetry, and split out here at the maintainer's request.
Explicitly not asked for here: a framework installer that rewrites global configuration for all tools. That was ruled out of scope on #859 and this issue does not reopen it. It asks only that an existing, intentional refusal fail loudly and cleanly, and that the state already shipped be written down.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Projects
- StatusShow more project fieldsDone
Activity
dkp-consult commentedon Sep 20, 2026
Follow-up on one thing I hedged in the body. I wrote that throwing from the constructor was "a plausible reason it escapes the command's error boundary, though I have not read that boundary". I have now read it, on
nexttoday, and it is the cause — so the hint can be treated as a fact.In
cli/src/presentation/commands/setup.ts, the flow is constructed fifteen lines above thetrythat guards everything else:SetupFlow's constructor callsvalidateScope(cli/src/contexts/framework/domain/setup-flow.ts:51), which throwsUserScopePluginModeErrorat line 94. Nothing between the command's action callback and that construction catches it, so the one validation error raised before line 158 is the one validation error the user never sees rendered — whileerrorHandlersits ready, already instantiated at line 120 and already used at line 69 for the flag parser.That also explains the asymmetry with
plugin install: the same kind of refusal, raised from inside a guarded call, renders fine.Two directions, both small, neither a prescription:
new SetupFlow(...)inside the existingtry, which renders this and any future constructor validation; or--scope/--pluginscombination where the other flag parsing already happens (line 124'sparseToolIds(cmdOptions, errorHandler)pattern), before anything is constructed.Verified against
nextat the time of writing; line numbers are current for that branch, not durable.docs(cli): record setup validation review
docs(cli): record independent setup validation review
docs(cli): keep the user-scope table to what setup refuses
fix(cli): render setup validation errors cleanly (#966)
blafourcade commentedon Oct 7, 2026
Delivered by #966, merged into
nextas a squash commit — which does not auto-close aCloses #891from a non-default base. Closing by hand.