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

  1. Coerce to object first: Object.keys(response ?? {})
  2. Guard with a check: if (value && typeof value === 'object') before calling Object.keys
  3. For strings, use value.length / indexing instead of Object.keys
  4. 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

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


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/18338fad70a70ce3. Report an issue: GitHub.