vercel/ai · critical

${terminalError}

Error message

${terminalError}

What it means

When the OpenCode event stream emits a session.error event, runPrompt records the formatted error in terminalError and rethrows it as a plain Error after the turn settles. This is the OpenCode server reporting a terminal failure for the turn (e.g. the underlying model/provider errored). The message is whatever OpenCode reported.

Source

Thrown at packages/harness-opencode/src/bridge/index.ts:778

    client,
    sessionId,
    start,
  });
  if (prompted.error) {
    eventsAbort.abort();
    turn.experimental_userMessages.close(
      new Error(`OpenCode prompt failed: ${formatError(prompted.error)}`),
    );
    throw new Error(`OpenCode prompt failed: ${formatError(prompted.error)}`);
  }
  const settlement = await turnSettled.promise;
  eventsAbort.abort();
  await eventLoop.catch(() => {});
  await userMessageLoop.catch(() => {});
  if (settlement === 'stream-ended') {
    throw new Error('OpenCode event stream ended before the turn settled.');
  }
  if (terminalError) throw new Error(terminalError);
  if (!sawFinishStep) {
    const emittedFallback = await emitContextFallback({
      client,
      sessionId,
      assistantBaseline,
      state,
      emit,
      emitContent: !sawContent,
    }).catch(() => false);
    if (!emittedFallback) {
      throw new Error(
        'OpenCode turn settled without a correlated assistant response.',
      );
    }
  }
  const finalSessionTokens =
    (await readSessionTokens({ client, sessionId }).catch(() => undefined)) ??
    latestSessionTokens;

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Read terminalError content in the message — it contains OpenCode's formatted provider/server error — and fix that underlying cause.
  2. Verify the LLM provider API keys and quotas configured on the OpenCode server.
  3. Check the model id in your start options is valid for the provider.
  4. Retry the turn after resolving provider-side issues.

Example fix

// before: expired provider key on opencode server
export ANTHROPIC_API_KEY=sk-expired
// after
export ANTHROPIC_API_KEY=sk-valid
opencode auth login
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight provider auth on the OpenCode server
const models = await fetch(`${base}/config/provider`).then(r => r.json());
if (!models?.length) throw new Error('No provider configured on OpenCode server');

Type guard

function isTerminalSessionError(e: unknown): e is Error {
  return e instanceof Error && !e.message.startsWith('OpenCode ') && e.message.length > 0 && /error|failed|quota|limit/i.test(e.message);
}

Try / catch

try {
  await bridge.runTurn(...);
} catch (e) {
  if (e instanceof Error && /quota|rate.?limit/i.test(e.message)) {
    await sleep(60_000); // back off provider rate limits
  } else if (e instanceof Error && /api.?key|auth/i.test(e.message)) {
    console.error('Fix provider credentials on the OpenCode server');
  } else throw e;
}

Prevention

When it happens

Trigger: During a turn, consumeEvents receives event.type === 'session.error' (or terminal failure from the server), e.g. the LLM provider returned an error, the model quota was exhausted, or a tool execution caused a fatal server error.

Common situations: Invalid or rate-limited LLM provider API key on the OpenCode server; provider outage mid-turn; model id not available for the configured provider; OpenCode internal error while executing tools.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/0d70e96d453a55c9. Report an issue: GitHub.