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
- Ensure the invoke params object is always { threadId: string, limit?: number, cursor?: string }.
- If forwarding a threadId from another surface, wrap it: { threadId, limit: DEFAULT_TRANSCRIPT_PAGE_LIMIT }.
- Add a unit test asserting the invoke payload shape before calling nodes.invoke.
- 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
- Always pass an object envelope { threadId, limit?, cursor? } to nodes.invoke for this command.
- Add a unit test asserting the invoke payload is a plain object.
- Use a schema helper (zod) at the call site for integration code.
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
- threadId is required
- ${key} must be a string
- ${key} must be at most ${maxLength} characters
- unknown Codex session catalog parameter: ${unknown}
- Codex session catalog parameters must be an object
AI-assisted analysis of openclaw/openclaw@01804a7531 (2026-08-12).
Data as JSON: /api/errors/7cef981c241500f4.
Report an issue: GitHub.