different-ai/openwork · error · InterpreterRuntimeError
Object.keys expects a data object or array.
Error message
Object.keys expects a data object or array.
What it means
The Object.keys branch of invokeObjectMethod accepts plain objects and arrays but throws for anything else (null, undefined, primitives, sandbox values). This is narrower than real JS, where Object.keys coerces primitives; CodeMode demands actual data containers.
Source
Thrown at packages/codemode/src/stdlib/object.ts:28
const requireObject = (): Record<string, unknown> => {
const value = boundedData(args[0], `Object.${name} input`)
if (isSandboxValue(value)) return {}
if (value === null || typeof value !== "object" || Array.isArray(value)) {
throw new InterpreterRuntimeError(`Object.${name} expects a data object.`, node)
}
return value as Record<string, unknown>
}
const guardedSet = (out: Record<string, unknown>, key: string, item: unknown): void => {
if (isBlockedMember(key)) throw new InterpreterRuntimeError(`Property '${key}' is not available in CodeMode.`, node)
out[key] = item
}
switch (name) {
case "keys": {
const value = boundedData(args[0], "Object.keys input")
if (isSandboxValue(value)) return []
if (Array.isArray(value)) return Object.keys(value)
if (value === null || typeof value !== "object") {
throw new InterpreterRuntimeError("Object.keys expects a data object or array.", node)
}
return Object.keys(value)
}
case "values":
return Object.values(requireObject())
case "entries":
return Object.entries(requireObject()).map(([key, item]) => [key, item])
case "hasOwn":
return Object.hasOwn(requireObject(), String(args[1]))
case "assign": {
const out: Record<string, unknown> = Object.create(null)
for (const source of args) {
if (source === null || source === undefined) continue
const value = boundedData(source, "Object.assign input")
if (isSandboxValue(value)) continue
if (value === null || typeof value !== "object" || Array.isArray(value)) {
throw new InterpreterRuntimeError("Object.assign expects data objects.", node)
}View on GitHub (pinned to 2b7df46e8a)
Solutions
- Coerce to object first: Object.keys(response ?? {})
- Guard with a check: if (value && typeof value === 'object') before calling Object.keys
- For strings, use value.length / indexing instead of Object.keys
- For primitives you truly want keys of, wrap: Object.keys(Object(value)) only if the sandbox allows — otherwise restructure
Example fix
// before
const names = Object.keys(config)
// after
const names = Object.keys(config ?? {}) Defensive patterns
Strategy: type-guard
Validate before calling
function assertKeysInput(v: unknown): asserts v is Record<string, unknown> | Array<unknown> {
if (v === null || (typeof v !== 'object')) throw new Error('Object.keys needs an object or array')
} Type guard
const isKeysInput = (v: unknown): v is object => v !== null && typeof v === 'object'
Try / catch
try {
return Object.keys(input)
} catch (e) {
if (e instanceof InterpreterRuntimeError && e.message.includes('expects a data object or array')) {
return []
}
throw e
} Prevention
- Default nullable inputs: Object.keys(x ?? {})
- Check typeof before calling Object.keys
- Handle scalar API responses upstream
- Prefer explicit shape parsing over Object statics on unknown data
When it happens
Trigger: Object.keys(null), Object.keys(undefined), Object.keys('abc'), Object.keys(42), Object.keys(true) inside a CodeMode script — typically when the input comes from a JSON.parse or API call that returned a scalar or null.
Common situations: API responses that are null on empty/missing resources then passed straight to Object.keys; string-typed config values assumed to be objects; optional fields left undefined.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Object.${name} expects a data object.
- Object.assign expects data objects.
- Object.fromEntries expects an array of [key, value] pairs.
- Object.fromEntries expects [key, value] pairs.
- String.${name} expects argument ${index + 1} to be a number.
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/18338fad70a70ce3.
Report an issue: GitHub.