paperclipai/paperclip · error · HarnessReconciliationError

thread/items/list returned a different turn

Error message

thread/items/list returned a different turn

What it means

readCodexTurnItems pages thread/items/list for a specific turnId and verifies each row's turnId matches. When the provider returns items belonging to a different turn, the library throws a HarnessReconciliationError, since mixing items across turns would corrupt turn reconstruction.

Source

Thrown at packages/paperclip-runner/src/drivers/codex/codex-history.ts:112

        );
      }
      return text(value.id);
    },
  );
}

export async function readCodexTurnItems(
  transport: Requester,
  threadId: string,
  turnId: string,
): Promise<Record<string, unknown>[]> {
  const entries = await pages(
    transport,
    "thread/items/list",
    { threadId, turnId, sortDirection: "asc" },
    (value) => {
      if (text(value.turnId) !== turnId)
        throw new HarnessReconciliationError(
          "thread/items/list returned a different turn",
        );
      return text(record(value.item).id);
    },
  );
  return entries.map((entry) => record(entry.item));
}

View on GitHub (pinned to 01ad858492)

Solutions

  1. Verify the turnId comes from readCodexTurnMetadata for the same threadId in the same reconciliation pass.
  2. Restart the app-server and re-run reconciliation; repeated-cursor filtering bugs are usually provider-side, so upgrade Codex if reproducible.
  3. Retry after the turn reaches a terminal status; listing items mid-turn can race with turn transitions.
  4. Fail closed — items from the wrong turn must not be merged into reconstruction.
Defensive patterns

Strategy: try-catch

Validate before calling

if (!threadTurnIds.has(turnId)) throw new Error('turnId not present in this thread\'s turn metadata');

Type guard

function isForeignTurnItem(v: Record<string, unknown>, turnId: string): boolean { return String(v.turnId ?? '') !== turnId; }

Try / catch

try { return await readCodexTurnItems(transport, threadId, turnId); } catch (e) { if (e instanceof HarnessReconciliationError && e.message.includes('different turn')) return retryAfterTerminal(transport, threadId, turnId); throw e; }

Prevention

When it happens

Trigger: A thread/items/list page includes an entry whose value.turnId differs from the requested turnId — provider filtering bug, cursor bleed across turns, or caller passing the wrong turnId to a shared thread.

Common situations: Codex app-server returning unfiltered items when a turn has no items of its own; race where a new turn starts while its predecessor's items are still being listed; wrong turnId from stale recovery metadata.

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/7ee57d8f4df0c3e7. Report an issue: GitHub.