Background
mapDomainErrorToErrorKind in packages/acp-bridge/src/status.ts (lifted from cli/src/serve/status.ts in PR #4298 / Wave 5 PR 22b/1) currently classifies two specific error variants by regex matching on error.message:
if (/agent channel closed/i.test(message)) {
return 'init_timeout';
}
if (/Cannot determine CLI entry path/i.test(message)) {
return 'missing_binary';
}
A TODO already calls this out as technical debt; PR 22b/1 review (github-actions Medium #4) asked for a tracked follow-up issue:
regex-based error classification can misclassify unrelated errors containing similar phrases.
Risk
Two real failure modes:
-
False positive misclassification: any future error whose .message happens to contain "agent channel closed" or "Cannot determine CLI entry path" gets routed to the wrong errorKind (and thus the wrong HTTP status code in the route layer). The substring check is broad enough that a wrapping error like RuntimeError: failed: <inner: agent channel closed> would also match.
-
Brittle to message wording changes: the throwing sites (BridgeTimeoutError at httpAcpBridge.ts:~1500 and defaultSpawnChannelFactory at ~4838) own these strings without a static guarantee that downstream classifiers stay aligned. A reasonable copy-edit (e.g. "ACP child closed unexpectedly" instead of "agent channel closed") silently degrades classification to a generic kind.
Proposed direction
Replace regex with typed error objects the bridge already throws:
BridgeTimeoutError (already a class, already exported) → mapDomainErrorToErrorKind checks error instanceof BridgeTimeoutError and returns 'init_timeout' directly. No string matching.
Cannot determine CLI entry path is currently a new Error(...) thrown inside defaultSpawnChannelFactory → introduce a new MissingCliEntryError class in acp-bridge/src/bridgeErrors.ts (parallel to the 11 existing error classes), throw it instead of a generic Error, and instanceof-branch the same way.
Existing structurally-distinct error classes (SessionNotFoundError, WorkspaceMismatchError, etc.) already follow this pattern; this issue extends the discipline to the last two regex holdouts.
Scope estimate
- ~50 LOC across
bridgeErrors.ts (new class) + bridge.ts or spawnChannel.ts (throw site) + status.ts (mapDomainErrorToErrorKind body) once PR 22b/2 lands the bridge core.
- Suitable as a sub-PR alongside PR 22b/2 (which lifts
defaultSpawnChannelFactory anyway), or a standalone post-22b/2 cleanup.
References
Background
mapDomainErrorToErrorKindinpackages/acp-bridge/src/status.ts(lifted fromcli/src/serve/status.tsin PR #4298 / Wave 5 PR 22b/1) currently classifies two specific error variants by regex matching onerror.message:A
TODOalready calls this out as technical debt; PR 22b/1 review (github-actions Medium #4) asked for a tracked follow-up issue:Risk
Two real failure modes:
False positive misclassification: any future error whose
.messagehappens to contain "agent channel closed" or "Cannot determine CLI entry path" gets routed to the wrongerrorKind(and thus the wrong HTTP status code in the route layer). The substring check is broad enough that a wrapping error likeRuntimeError: failed: <inner: agent channel closed>would also match.Brittle to message wording changes: the throwing sites (
BridgeTimeoutErrorathttpAcpBridge.ts:~1500anddefaultSpawnChannelFactoryat~4838) own these strings without a static guarantee that downstream classifiers stay aligned. A reasonable copy-edit (e.g. "ACP child closed unexpectedly" instead of "agent channel closed") silently degrades classification to a generic kind.Proposed direction
Replace regex with typed error objects the bridge already throws:
BridgeTimeoutError(already a class, already exported) →mapDomainErrorToErrorKindcheckserror instanceof BridgeTimeoutErrorand returns'init_timeout'directly. No string matching.Cannot determine CLI entry pathis currently anew Error(...)thrown insidedefaultSpawnChannelFactory→ introduce a newMissingCliEntryErrorclass inacp-bridge/src/bridgeErrors.ts(parallel to the 11 existing error classes), throw it instead of a genericError, andinstanceof-branch the same way.Existing structurally-distinct error classes (
SessionNotFoundError,WorkspaceMismatchError, etc.) already follow this pattern; this issue extends the discipline to the last two regex holdouts.Scope estimate
bridgeErrors.ts(new class) +bridge.tsorspawnChannel.ts(throw site) +status.ts(mapDomainErrorToErrorKindbody) once PR 22b/2 lands the bridge core.defaultSpawnChannelFactoryanyway), or a standalone post-22b/2 cleanup.References
acp-bridgeverbatim