TS SDK: config/outputSchema plain-object guard silently accepts Date/Map/RegExp, corrupting serialized output
Description
isPlainObject() in sdk/typescript/src/exec.ts and isJsonObject() in sdk/typescript/src/outputSchemaFile.ts are used as the validation gate before serializing user-supplied values (config overrides, turnOptions.outputSchema) to JSON/TOML. Both are defined identically:
function isPlainObject(value: unknown): value is CodexConfigObject {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
This accepts any non-array object — including Date, Map, RegExp, or any class instance — not just plain {}-style objects. Since these types don't round-trip through JSON.stringify/Object.entries the way a plain object does, the validation silently passes and the resulting output is wrong instead of raising the descriptive error the code clearly intends ("Codex config overrides must be a plain object" / "outputSchema must be a plain JSON object").
Steps to reproduce
function isPlainObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function toTomlValue(value, path) {
if (typeof value === "string") return JSON.stringify(value);
if (typeof value === "number") return `${value}`;
if (typeof value === "boolean") return value ? "true" : "false";
if (Array.isArray(value)) return `[${value.map((v, i) => toTomlValue(v, `${path}[${i}]`)).join(", ")}]`;
if (isPlainObject(value)) {
const parts = [];
for (const [k, c] of Object.entries(value)) {
if (c === undefined) continue;
parts.push(`${k} = ${toTomlValue(c, `${path}.${k}`)}`);
}
return `{${parts.join(", ")}}`;
}
throw new Error(`Unsupported value at ${path}`);
}
console.log(isPlainObject(new Date())); // true (should be false)
console.log(toTomlValue(new Date("2024-01-01"), "ts")); // "{}" -- value silently dropped, no error
Expected behavior
Passing a Date/Map/RegExp/other non-plain object as a config override value or as turnOptions.outputSchema should throw a clear error ("Codex config overrides must be a plain object" / "outputSchema must be a plain JSON object"), the same as passing a string or number would if it reached the object branch.
Actual behavior
The value is accepted, then silently serialized to {} (for Map/RegExp) or a bare string (for Date) with no error — the caller has no signal that their config was dropped.
Environment
- Commit:
7c37479(main, 2026-08-27) - Files:
sdk/typescript/src/exec.ts:345-347,sdk/typescript/src/outputSchemaFile.ts:38-40 sdk/typescript/tests/has no coverage forisPlainObject/isJsonObject/createOutputSchemaFile(confirmed via grep across the wholetests/directory).
Suggested fix
Tighten both guards to check the prototype instead of just excluding arrays:
function isPlainObject(value: unknown): value is CodexConfigObject {
- return typeof value === "object" && value !== null && !Array.isArray(value);
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
+ return false;
+ }
+ const proto = Object.getPrototypeOf(value);
+ return proto === Object.prototype || proto === null;
}
(apply the same change to isJsonObject in outputSchemaFile.ts)