paperclipai/paperclip · error

OpenCode resumed a different session

Error message

OpenCode resumed a different session

What it means

When resuming, the driver fetches the existing provider session by id and verifies the returned record's id matches the requested providerSessionId. It throws this error if the OpenCode server returns a record whose id differs (or a non-record), meaning the server would resume a different conversation than the one stored.

Source

Thrown at packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts:314

          cwd,
          trace,
          dispatch: (call) => {
            if (session === null)
              throw new Error("OpenCode session is not ready for tool calls");
            return session.dispatchTool(call);
          },
        });
        const fetcher = this.#options.fetch ?? globalThis.fetch;
        let providerSessionId =
          snapshot?.providerSessionId ?? snapshot?.driverSessionId ?? null;
        if (providerSessionId !== null) {
          const existing = await api(
            fetcher,
            runtime,
            `/session/${encodeURIComponent(providerSessionId)}`,
          );
          if (!isRecord(existing) || text(existing.id) !== providerSessionId)
            throw new Error("OpenCode resumed a different session");
        } else {
          const created = await api(fetcher, runtime, "/session", {
            method: "POST",
            body: JSON.stringify({ title: `Paperclip ${input.runId}` }),
          });
          providerSessionId = text(record(created).id);
          if (!providerSessionId)
            throw new Error("OpenCode session creation omitted its id");
        }
        session = new OpenCodeHarnessSession({
          runtime,
          fetcher,
          runId: input.runId,
          normalizedSessionId: input.normalizedSessionId,
          providerSessionId,
          workingDirectory: cwd,
          runnerInstanceId:
            this.#options.runnerInstanceId ??

View on GitHub (pinned to 01ad858492)

Solutions

  1. Verify the OpenCode server still has the session (GET /session/<id>) and that its id matches exactly.
  2. If the server lost the session, create a new session instead of resuming (clear the stale providerSessionId).
  3. Point all requests at the same OpenCode server instance (sticky routing / single instance).
  4. Log the returned existing.id and requested id to spot normalization or encoding differences and fix the persistence layer.

Example fix

// before
const existing = await api(fetcher, runtime, `/session/${providerSessionId}`); // mismatched id returned, throws
// after
const existing = await api(fetcher, runtime, `/session/${encodeURIComponent(providerSessionId)}`);
if (!isRecord(existing) || text(existing.id) !== providerSessionId) {
  providerSessionId = text(record(await api(fetcher, runtime, '/session', { method: 'POST', body: JSON.stringify({ title: `Paperclip ${input.runId}` }) })).id);
}
Defensive patterns

Strategy: fallback

Validate before calling

const existing = await api(fetcher, runtime, `/session/${encodeURIComponent(providerSessionId)}`);
const matches = isRecord(existing) && text(existing.id) === providerSessionId;
if (!matches) console.warn(`OpenCode session ${providerSessionId} missing/mismatched; will create a new session`);

Type guard

const sessionMatches = (r: unknown, id: string): r is { id: string } => isRecord(r) && text((r as { id?: unknown }).id) === id;

Try / catch

try { await resumeOrOpen(providerSessionId); } catch (e) { if ((e as Error).message === 'OpenCode resumed a different session') { await clearStoredProviderSessionId(); await openSession(input); } else throw e; }

Prevention

When it happens

Trigger: GET /session/<providerSessionId> returns a session object with a different id — typically after the server restarted with a new session store, session id collision/normalization mismatch, or a proxy returning a default/first session.

Common situations: OpenCode server restarted losing session state, providerSessionId persisted from an older server instance, session ids with encodings mangled by encodeURIComponent on one side, load balancer routing to another instance.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/415115a9d29311c1. Report an issue: GitHub.