vercel/ai · critical

OpenCode prompt failed: ${formatError(prompted.error)}

Error message

OpenCode prompt failed: ${formatError(prompted.error)}

What it means

runPrompt sends the user prompt to the OpenCode session with legacySessionPrompt. If the prompt API returns an error result, the bridge aborts the event stream, closes the user-message queue with this error, and throws it. The turn fails immediately with the OpenCode server's formatted error message.

Source

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

        message.accept();
      } catch (error) {
        message.reject(error);
      } finally {
        submittingUserMessage = false;
      }
    }
  })();
  const prompted = await legacySessionPrompt({
    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);

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Inspect formatError output in the message for the server-side cause (invalid model, busy session, etc.).
  2. Verify the session id is still valid and no other prompt is in flight for it.
  3. Check the model/provider configured in start options exists on the OpenCode server.
  4. Retry the turn if the failure was transient (e.g. 502 from the underlying LLM provider).

Example fix

// before: stale session id reused across restarts
await bridge.runTurn({ sessionId: oldId, prompt }); // OpenCode prompt failed: session not found
// after: let the bridge create a fresh session
await bridge.runTurn({ prompt }); // new session created automatically
Defensive patterns

Strategy: try-catch

Validate before calling

const session = await fetch(`${base}/session/${sessionId}`).then(r => r.ok);
if (!session) throw new Error(`Session ${sessionId} does not exist before prompting`);

Type guard

function isPromptError(e: unknown): e is Error {
  return e instanceof Error && e.message.startsWith('OpenCode prompt failed:');
}

Try / catch

try {
  await bridge.runTurn({ sessionId, prompt });
} catch (e) {
  if (isPromptError(e)) {
    console.error('Prompt error detail:', e.message.replace('OpenCode prompt failed: ', ''));
    // recreate session or fix model config, then retry
  } else throw e;
}

Prevention

When it happens

Trigger: Calling a turn (runTurn -> runPrompt) when legacySessionPrompt resolves prompted.error — server rejected the prompt request (invalid session id, session already busy, provider/model misconfigured, HTTP 4xx/5xx from OpenCode).

Common situations: Prompting a stale/deleted session id; a model/provider name not configured on the OpenCode server; concurrent prompts to the same session; OpenCode server returning 409/500 while processing.

Related errors


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