paperclipai/paperclip · error
OpenCode recovery failed
Error message
OpenCode recovery failed
What it means
When opening with a resume path, the proxy asks the driver to recover a previous session from a snapshot; if the driver reports recovered=false or returns no session, the proxy throws, preferring the driver's reason string and falling back to the generic 'OpenCode recovery failed' message.
Source
Thrown at packages/paperclip-runner/src/cli/opencode-app-server-proxy.ts:154
// runnerd gives this proxy its own process group. Keep `opencode serve` in
// that same group so runnerd's TERM/KILL fallback cannot orphan it.
isolateProcessGroup: false,
});
if (resume) {
const threadId = text(params.threadId);
const snapshot: PersistedHarnessSession = {
driverKind: "opencode_server",
driverSessionId: threadId,
providerSessionId: threadId,
runId: process.env.PAPERCLIP_RUN_ID ?? "runnerd-run",
normalizedSessionId:
process.env.PAPERCLIP_NORMALIZED_SESSION_ID ?? threadId,
activeTurnId: null,
lastSourceSequence: 0,
};
const recovered = await driver.recoverSession(snapshot);
if (!recovered.recovered || !recovered.session)
throw new Error(recovered.reason ?? "OpenCode recovery failed");
session = recovered.session;
} else {
session = await driver.openSession({
runId: process.env.PAPERCLIP_RUN_ID ?? "runnerd-run",
normalizedSessionId:
process.env.PAPERCLIP_NORMALIZED_SESSION_ID ?? `runnerd-${Date.now()}`,
workingDirectory: cwd,
});
}
eventPump = pumpEvents(session);
void eventPump.catch((error) => failProxy(error));
return threadResponse(
session.ids().providerSessionId ?? session.ids().driverSessionId,
);
}
function threadResponse(id: string): Record<string, unknown> {
return { thread: { id, sessionId: id, cwd }, model: activeModel };View on GitHub (pinned to 01ad858492)
Solutions
- Check the thrown message/recovered.reason for the specific recovery cause
- Start a fresh session instead of resuming (drop the resume path so openSession runs)
- Verify the normalized session id / thread id matches an existing runtime session
Example fix
// before
const s = await resumeSession(threadId); // throws if unrecoverable
// after
try { s = await resumeSession(threadId); }
catch { s = await openSession({ cwd, model }); } // fresh session fallback Defensive patterns
Strategy: fallback
Validate before calling
const snapshot = buildSnapshot(threadId); const probe = await driver.probeSession?.(snapshot.normalizedSessionId); if (probe && !probe.exists) return openFreshSession();
Type guard
function isRecoverable(r: { recovered: boolean; session?: unknown; reason?: string }): r is { recovered: true; session: NonNullable<unknown> } { return r.recovered && r.session != null; } Try / catch
try { await openWithRecovery(snapshot); }
catch (e) { if (String(e.message).includes('OpenCode recovery failed')) { log.warn('recovery failed, opening fresh session', e.message); await openFreshSession(); } else throw e; } Prevention
- Verify session persistence exists before attempting resume
- Surface recovered.reason in logs to distinguish stale id vs missing data
- Prefer fresh-session fallback in interactive flows where history is non-critical
When it happens
Trigger: driver.recoverSession(snapshot) returns { recovered: false } or { recovered: true, session: null } for the snapshot built from PAPERCLIP_NORMALIZED_SESSION_ID / threadId and lastSourceSequence 0.
Common situations: Resuming a session whose persisted runtime data was deleted; a stale or mismatched session id after a runtime reset; the underlying OpenCode app server no longer recognizes the thread.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- OpenCode session is not ready for tool calls
- OpenCode resumed a different session
- OpenCode session creation omitted its id
- opencode_run_attach_busy
- native_session_recovery_failed
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/fc919369b185da4f.
Report an issue: GitHub.