Skip to content

fix(cli): setup --scope user --plugins throws an uncaught UserScopePluginModeError instead of rendering a validation error #891

Closed

Description

@dkp-consult

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 — throws UserScopePluginModeError, from the constructor by way of validateScope (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

  1. From a project with an AIDD manifest, run aidd setup --scope user --plugins <any plugin>.
  2. Read the stack trace.
  3. 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 claude and codex are, copilot and cursor are 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 cursor cell of the plugin install row 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.

Activity

  1. dkp-consult commented on Sep 20, 2026

    @dkp-consult
    ContributorAuthor

    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 next today, 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 the try that guards everything else:

    143:      const flow = new SetupFlow({ …, pluginMode, pluginNames, …, scope });
          …
    158:      try {
          …            // createDeps, SetupUseCase(...).execute(flow), the printers
    181:        errorHandler.handle(error);

    SetupFlow's constructor calls validateScope (cli/src/contexts/framework/domain/setup-flow.ts:51), which throws UserScopePluginModeError at 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 — while errorHandler sits 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:

    • move the new SetupFlow(...) inside the existing try, which renders this and any future constructor validation; or
    • validate the --scope/--plugins combination where the other flag parsing already happens (line 124's parseToolIds(cmdOptions, errorHandler) pattern), before anything is constructed.

    Verified against next at the time of writing; line numbers are current for that branch, not durable.

  2. added theissue type on Sep 22, 2026
  3. moved this from Ideation to Todo in AIDD Roadmapon Sep 24, 2026
  4. blafourcade commented on Oct 7, 2026

    @blafourcade
    Contributor

    Delivered by #966, merged into next as a squash commit — which does not auto-close a Closes #891 from a non-default base. Closing by hand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    Priority

    Medium

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions