Skip to content

[FEATURE]: CLI: expose machine-readable signal for provider-side retryable failures #32633

Closed as not planned

Description

@NathanDrake2406

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

Context

OpenCode is sometimes invoked by automation (e.g. GitHub Actions wrappers) that would like to retry transient provider-side failures such as:

  • Stream drops / aborts
  • Provider-side cancellations / timeouts

Today the CLI emits human-readable stderr like The operation was canceled. and exits with code 1, so a wrapper cannot distinguish a retryable provider blip from a genuine failure without string parsing the output.

Request

Please expose a machine-readable retry signal that a process wrapper can consume. Any of these would work:

  1. Distinct exit codes for retryable provider failures (e.g. exit 2 for provider abort, exit 3 for stream timeout), while keeping exit 1 for unknown/other failures.
  2. Structured error summary written to a known path on failure, e.g. .opencode/error.json with {"type": "AbortedError", "retryable": true}.
  3. Prefixed error line in stderr that wrappers can grep for, e.g. PROVIDER_RETRYABLE: stream ended unexpectedly.

Why this matters

String matching on stderr is inherently fragile — repo/test output can accidentally contain the same phrases. A first-class signal would let wrappers retry safely without heuristic output parsing.

Related

This request comes from work on a GitHub Actions wrapper that currently has to parse stderr tails to decide whether to retry opencode github run. A machine-readable signal would let us remove that heuristic.

Activity

  1. github-actions commented on Jun 17, 2026

    @github-actions
    Contributor

    This issue doesn't fully meet our contributing guidelines.

    What needs to be fixed:

    • This appears to be a feature request, but it does not use the feature request template. The title should start with [FEATURE]: and the issue should include the required verification checkbox confirming you have searched for existing issues.

    Please edit this issue to address the above within 2 hours, or it will be automatically closed.

    If you believe this was flagged incorrectly, please let a maintainer know.

  2. changed the title [-]CLI: expose machine-readable signal for provider-side retryable failures[/-] [+][FEATURE]: CLI: expose machine-readable signal for provider-side retryable failures[/+] on Jun 17, 2026
  3. github-actions commented on Jun 17, 2026

    @github-actions
    Contributor

    Thanks for updating the issue! It now follows the contributing guidelines and looks good. We appreciate the clear description of the use case and the concrete options you've proposed.

  4. chernistry commented on Aug 12, 2026

    @chernistry

    Adding a second consumer, and one failure class that isn't yet enumerated here.

    We maintain bernstein, an Apache-2.0 orchestrator that runs CLI coding agents in
    parallel git worktrees; OpenCode already ships as one of its adapters. We run:

    opencode run -m <model-id> --format json "<prompt>"
    

    Beyond the provider-side failures above, there's a third class where the exit code is
    actively misleading rather than merely undifferentiated: a run whose tool calls were
    denied by the permission system exits 0. In packages/opencode/src/cli/cmd/run.ts,
    error is assigned only from session.error (L776-786) and consulted at L837; the
    permission auto-reject path (L796-815) never sets it. #36413 reproduces this end to end.

    The --format json stream doesn't close the gap either. It carries tool_use, step_start,
    step_finish, text, reasoning, error (L720-784) but has no terminal event — the loop
    breaks on session idle (L789-793) without emitting, so the stream just stops. A consumer
    can't separate completion from abort, and has to infer totals by accumulating
    step_finish parts.

    So, a fourth option alongside the three above: emit a final event in the existing
    envelope, at the point the loop already breaks.

    {"type":"result","sessionID":"ses_...","outcome":"success|error|blocked",
     "reason":"stop|length|tool-calls|aborted",
     "tokens":{"input":0,"output":0,"reasoning":0,"cache":{"read":0,"write":0}},
     "cost":0.0}
    

    The values already exist at that point — step-finish carries reason, tokens and cost.
    Consumers that ignore unknown types are unaffected, so it's additive.

    Happy to send the PR. Would you prefer distinct exit codes, a terminal result event,
    or both — and should result be gated behind --format json?

    If any of this is already reachable and we've missed it, that answer alone unblocks us.

  5. github-actions commented on Oct 12, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions