fix(run): exit non-zero on session.error so CI wrappers see failures#71
Merged
Conversation
The `aictrl run` command's session.error event handler logged the error and accumulated it into a local string but never set `process.exitCode`. On auth/provider failures, the subsequent `session.status: idle` broke the run loop into the success branch and the process exited with code 0 — while stdout printed "Authentication Failed" or similar. This silently broke CI workflows wrapping `aictrl run` (observed on aictrl-dev/aictrl's "AI Review" workflow on 2026-05-20): step conclusion `success`, no actual work, badge stays green. Fix: set `process.exitCode = 1` when the session.error event fires for the primary session. Sub-agent failures keep their existing behavior of flowing into the parent agent. Use `exitCode` (not `process.exit`) so the event loop drains naturally — session_complete still emits before the process terminates. Regression test (test/cli/run-session-error-exit.test.ts) statically asserts the handler block sets a non-zero exit code, mirroring the existing run-event-race.test.ts style. Closes #70 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
Contributor
Author
|
Note on the failing `Aictrl AI Review` check: it's failing in the `Install Aictrl CLI` step (`npm install @aictrl/cli@latest` → `EUNSUPPORTEDPROTOCOL: Unsupported URL Type "workspace:": workspace:*`), not on this PR's code change. The `verify` check (typecheck + tests) passes. The published `@aictrl/cli@0.3.3` manifest ships unresolved `workspace:*` deps that plain `npm install` can't handle. Same failure affects every PR opened on this repo since 0.3.3 published — filed as #72 with reproduction, root cause hypothesis, and acceptance criteria. PR #71's change has been verified locally:
Suggest merging on `verify` ✓ alone, or waiting for #72 to land if you'd rather have AI Review running first. |
This was referenced May 21, 2026
byapparov
added a commit
that referenced
this pull request
May 21, 2026
v0.3.4 release (#71 + #72 fixes) couldn't actually publish @aictrl/cli because the publish workflow doesn't auto-stamp AICTRL_VERSION into the main package.json `version` fields — only platform binaries get the release version. On v0.3.4 the workflow tolerated "already-published" errors for cli 0.3.3 / util 1.2.16 / sdk 0.1.2 and the smoke test correctly caught it (ETARGET: No matching version found for @aictrl/cli@0.3.4). Bump cli and util so v0.3.5 actually publishes new versions carrying the resolver + exit-code fixes. Skip sdk: its catalog: deps are only in devDependencies, ignored by npm consumers, so 0.1.2 is functionally installable. Follow-up worth filing: publish.yml should stamp AICTRL_VERSION into all package manifests, not just platform binaries, so future releases don't need manual version bumps. Refs #71 #72 Co-authored-by: Bulat Yapparov <by4pparov@yandex.ru> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
aictrl run'ssession.errorhandler inrun.tslogged the error and stashed it into a local string but never setprocess.exitCode— so when the model session subsequently emittedsession.status: idle, the run loop broke into the success branch and the process exited 0.aictrl runshows stepconclusion: successwhile the actual work (review, agent invocation, etc.) silently failed.process.exitCode = 1whensession.errorfires for the primary session (sub-agent failures keep their existing behavior). UsesexitCoderather thanprocess.exit()sosession_completestill emits before termination.Root cause
packages/cli/src/cli/cmd/run.ts:559-572— the handler block only updated the localerroraccumulator string and calledUI.error(). It did not propagate to the process exit code, so the success branch ofloopDone.then(...)(line 703) ran on every error.Trace for the auth-failure case observed in production:
session/processor.ts:388publishessession.error, sets statusidle.run.ts:559handler logs but doesn't touchexitCode.run.ts:614session.status idlebreaks the loop.run.ts:703success branch — emitssession_complete, returns.The neighboring error paths (
promptResult.catchat line 768,loopDone.catchat line 721) already setexitCode = 1for synchronous failures; thesession.errorevent path was the only gap.Reproduction (from aictrl-dev/aictrl#2058 investigation)
After the fix: exit
1.Test plan
packages/cli/test/cli/run-session-error-exit.test.ts) — static lock asserting the handler block sets a non-zero exit code, mirroring the existingrun-event-race.test.tsanti-pattern-lock style. Fails onorigin/main(verified locally), passes on this branch.bun test test/cli/— 69/69).bun typecheckpasses.Acceptance criteria (from #70)
aictrl runwith an invalidZHIPU_API_KEYexits non-zero — flows throughsession.errorfor primary session →exitCode = 1aictrl runwith a network failure to the provider exits non-zero — same path (provider-side error →MessageV2.fromError→Session.Event.Error)Related
1as a reasonable baseline; if feat: lite headless binary for Cloud Run #4 lands a structured convention, the constant can be aligned then without redesigning the propagation path.Closes #70
🤖 Generated with Claude Code