openclaw/openclaw · error · CatalogParamsError

Codex session read parameters must be an object

Error message

Codex session read parameters must be an object

What it means

Thrown by readNodeTranscriptParams when validating the parsed JSON params for the node-host transcript command. The very first check requires the params to be a plain object (isRecord); anything else (array, string, number, null, boolean) is rejected before any field is read. This is a contract guard for the node-invoke RPC shape.

Source

Thrown at extensions/codex/src/session-catalog.ts:731

            throw error;
          }
          throw new Error("Codex app-server transcript is unavailable", { cause: error });
        }
      },
    },
    createCodexTerminalNodeHostCommand(control, configSources),
  ];
}

type CodexNodeSessionTranscriptParams = {
  threadId: string;
  cursor?: string;
  limit: number;
};

function readNodeTranscriptParams(value: unknown): CodexNodeSessionTranscriptParams {
  if (!isRecord(value)) {
    throw new CatalogParamsError("Codex session read parameters must be an object");
  }
  requireOnlyKeys(value, new Set(["threadId", "cursor", "limit"]));
  const threadId = readBoundedOptionalString(value, "threadId", MAX_SESSION_ID_LENGTH);
  if (!threadId) {
    throw new CatalogParamsError("threadId is required");
  }
  const cursor = readBoundedOptionalString(value, "cursor", MAX_CURSOR_LENGTH);
  const limit = readBoundedLimit(
    value.limit,
    "limit",
    DEFAULT_TRANSCRIPT_PAGE_LIMIT,
    MAX_TRANSCRIPT_PAGE_LIMIT,
  );
  return { threadId, limit, ...(cursor ? { cursor } : {}) };
}

function readBoundedLimit(value: unknown, key: string, fallback: number, max: number): number {
  if (value === undefined) {

View on GitHub (pinned to 01804a7531)

Solutions

  1. Ensure the invoke params object is always { threadId: string, limit?: number, cursor?: string }.
  2. If forwarding a threadId from another surface, wrap it: { threadId, limit: DEFAULT_TRANSCRIPT_PAGE_LIMIT }.
  3. Add a unit test asserting the invoke payload shape before calling nodes.invoke.
  4. Validate the envelope with a schema helper (zod or the existing requireOnlyKeys path) at the call site.

Example fix

// before
await runtime.nodes.invoke({ nodeId, command: CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND, params: threadId });
// after
await runtime.nodes.invoke({ nodeId, command: CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND, params: { threadId, limit: 50 } });
Defensive patterns

Strategy: validation

Validate before calling

function isValidTranscriptParams(v: unknown): v is { threadId: string; limit?: number; cursor?: string } {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}
if (!isValidTranscriptParams(params)) throw new Error('params must be an object');

Type guard

function isTranscriptParams(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

Prevention

When it happens

Trigger: runtime.nodes.invoke is called with a params value that JSON-parses to a non-object (e.g. a bare string threadId, an array, or null) for the CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND. parseJsonParams succeeds but isRecord returns false at line 730.

Common situations: Caller passes a raw threadId string instead of { threadId, limit }; caller sends an array of ids; malformed hand-constructed invoke payload; a different command's param shape was copy-pasted; integration test fixture with wrong envelope.

Related errors


AI-assisted analysis of openclaw/openclaw@01804a7531 (2026-08-12). Data as JSON: /api/errors/7cef981c241500f4. Report an issue: GitHub.