{"record":{"id":"fc919369b185da4f","repo":"paperclipai/paperclip","slug":"opencode-recovery-failed","errorCode":null,"errorMessage":"OpenCode recovery failed","messagePattern":"OpenCode recovery failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/cli/opencode-app-server-proxy.ts","lineNumber":154,"sourceCode":"    // runnerd gives this proxy its own process group. Keep `opencode serve` in\n    // that same group so runnerd's TERM/KILL fallback cannot orphan it.\n    isolateProcessGroup: false,\n  });\n  if (resume) {\n    const threadId = text(params.threadId);\n    const snapshot: PersistedHarnessSession = {\n      driverKind: \"opencode_server\",\n      driverSessionId: threadId,\n      providerSessionId: threadId,\n      runId: process.env.PAPERCLIP_RUN_ID ?? \"runnerd-run\",\n      normalizedSessionId:\n        process.env.PAPERCLIP_NORMALIZED_SESSION_ID ?? threadId,\n      activeTurnId: null,\n      lastSourceSequence: 0,\n    };\n    const recovered = await driver.recoverSession(snapshot);\n    if (!recovered.recovered || !recovered.session)\n      throw new Error(recovered.reason ?? \"OpenCode recovery failed\");\n    session = recovered.session;\n  } else {\n    session = await driver.openSession({\n      runId: process.env.PAPERCLIP_RUN_ID ?? \"runnerd-run\",\n      normalizedSessionId:\n        process.env.PAPERCLIP_NORMALIZED_SESSION_ID ?? `runnerd-${Date.now()}`,\n      workingDirectory: cwd,\n    });\n  }\n  eventPump = pumpEvents(session);\n  void eventPump.catch((error) => failProxy(error));\n  return threadResponse(\n    session.ids().providerSessionId ?? session.ids().driverSessionId,\n  );\n}\n\nfunction threadResponse(id: string): Record<string, unknown> {\n  return { thread: { id, sessionId: id, cwd }, model: activeModel };","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/cli/opencode-app-server-proxy.ts#L136-L172","documentation":"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.","triggerScenarios":"driver.recoverSession(snapshot) returns { recovered: false } or { recovered: true, session: null } for the snapshot built from PAPERCLIP_NORMALIZED_SESSION_ID / threadId and lastSourceSequence 0.","commonSituations":"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.","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"],"exampleFix":"// before\nconst s = await resumeSession(threadId); // throws if unrecoverable\n// after\ntry { s = await resumeSession(threadId); }\ncatch { s = await openSession({ cwd, model }); } // fresh session fallback","handlingStrategy":"fallback","validationCode":"const snapshot = buildSnapshot(threadId);\nconst probe = await driver.probeSession?.(snapshot.normalizedSessionId);\nif (probe && !probe.exists) return openFreshSession();","typeGuard":"function isRecoverable(r: { recovered: boolean; session?: unknown; reason?: string }): r is { recovered: true; session: NonNullable<unknown> } { return r.recovered && r.session != null; }","tryCatchPattern":"try { await openWithRecovery(snapshot); }\ncatch (e) { if (String(e.message).includes('OpenCode recovery failed')) { log.warn('recovery failed, opening fresh session', e.message); await openFreshSession(); } else throw e; }","preventionTips":["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"],"tags":["opencode","recovery","session"],"backgroundTag":"resource-not-found","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}