{"record":{"id":"0ba9cfabd28767c6","repo":"paperclipai/paperclip","slug":"opencode-session-is-not-ready-for-tool-calls","errorCode":null,"errorMessage":"OpenCode session is not ready for tool calls","messagePattern":"OpenCode session is not ready for tool calls","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts","lineNumber":300,"sourceCode":"    const trace = await createProviderTraceFileSink({\n      path: this.#options.environment?.PAPERCLIP_PROVIDER_TRACE_PATH,\n      provider: \"opencode\",\n      channel: \"typescript_opencode_native\",\n      maxBytes: this.#options.environment?.PAPERCLIP_PROVIDER_TRACE_MAX_BYTES,\n    });\n    let lastError: unknown = null;\n    for (let attempt = 1; attempt <= 3; attempt += 1) {\n      let session: OpenCodeHarnessSession | null = null;\n      let runtime: OpenCodeRuntime | null = null;\n      try {\n        runtime = await startRuntime({\n          options: this.#options,\n          root,\n          cwd,\n          trace,\n          dispatch: (call) => {\n            if (session === null)\n              throw new Error(\"OpenCode session is not ready for tool calls\");\n            return session.dispatchTool(call);\n          },\n        });\n        const fetcher = this.#options.fetch ?? globalThis.fetch;\n        let providerSessionId =\n          snapshot?.providerSessionId ?? snapshot?.driverSessionId ?? null;\n        if (providerSessionId !== null) {\n          const existing = await api(\n            fetcher,\n            runtime,\n            `/session/${encodeURIComponent(providerSessionId)}`,\n          );\n          if (!isRecord(existing) || text(existing.id) !== providerSessionId)\n            throw new Error(\"OpenCode resumed a different session\");\n        } else {\n          const created = await api(fetcher, runtime, \"/session\", {\n            method: \"POST\",\n            body: JSON.stringify({ title: `Paperclip ${input.runId}` }),","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts#L282-L318","documentation":"The OpenCode server driver wires a dispatch callback that forwards tool calls to the active session. It throws this error if a tool call arrives while the internal session handle is still null, i.e. before the HTTP session fetch/create sequence has completed and a OpenCodeHarnessSession has been assigned.","triggerScenarios":"A tool call is dispatched during #open before the async /session or /session/:id API call resolves, e.g. a re-entrant or early callback from the harness, or resume path throwing before session assignment.","commonSituations":"Race between harness initialization and first tool call, provider session lookup taking long while the agent already emits calls, bugs in mocked/fake fetchers that dispatch early.","solutions":["Ensure no tool calls are dispatched until the driver's open/openSession promise resolves.","Check that the resume endpoint (`GET /session/:id`) isn't hanging or failing silently, delaying session assignment.","Upgrade the driver/harness so session assignment happens before attaching the tool dispatcher.","Reproduce with trace logging to confirm whether the call arrives before session creation and fix the ordering."],"exampleFix":"// before\nconst driver = new OpenCodeServerDriver(...);\ndriver.dispatchTool(call); // session not yet opened\n// after\nawait driver.openSession(input);\ndriver.dispatchTool(call);","handlingStrategy":"try-catch","validationCode":"// gate tool dispatch on session readiness\nif (!driver.isSessionReady()) throw new Error('wait for openSession() before dispatching tool calls');","typeGuard":"const sessionReady = (d: { isSessionReady?(): boolean }): boolean => d.isSessionReady?.() ?? false;","tryCatchPattern":"try { return dispatchCall(call); } catch (e) { if ((e as Error).message === 'OpenCode session is not ready for tool calls') { await driver.openSession(input); return dispatchCall(call); } throw e; }","preventionTips":["Always await openSession/open before issuing any tool call","Queue tool calls until session assignment completes instead of dispatching immediately","Keep tool-dispatch wiring inside the resolved-session callback path","Add trace logs showing open-start and session-assigned timestamps to catch ordering races"],"tags":["opencode","session","race-condition","initialization"],"backgroundTag":"invalid-state-transition","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}