Skip to content

tech-debt(acp-bridge): replace mapDomainErrorToErrorKind regex matching with typed errors #4299

@doudouOUC

Description

@doudouOUC

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:

  1. 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.

  2. 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

Metadata

Metadata

Assignees

Labels

category/coreCore engine and logicpriority/P2Medium - Moderately impactful, noticeable problemscope/non-interactiveNon-interactive modetype/enhancementNon-bug improvement or optimization

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions