different-ai/openwork · error · ToolRuntimeError

InvalidDataValue

InvalidDataValue

Error message

${label} exceeds the maximum value depth of ${MAX_VALUE_DEPTH}.

What it means

CodeMode bounds every value crossing the tool/interpreter boundary (copyIn/copyBounded/copied) to a maximum nesting depth (MAX_VALUE_DEPTH). Deeply nested objects/arrays beyond that limit are rejected with this InvalidDataValue ToolRuntimeError to prevent stack exhaustion and runaway memory when copying values in or out of the sandbox.

Source

Thrown at packages/codemode/src/tool-runtime.ts:182

 * - **Intra-sandbox checkpoint** (`preserveSandboxValues` true; see `boundedData` in
 *   codemode.ts): standard-library value instances pass through untouched (treated as leaves,
 *   contents not walked), so values flowing through `Object.*` helpers, coercion inputs, and
 *   other in-sandbox checkpoints stay fully usable (`.getTime()`, `.has()`, ...).
 *
 * Both modes reject un-awaited promises with an await-hinting diagnostic.
 */
export const copyIn = (value: unknown, label: string, preserveSandboxValues = false): unknown =>
  copyBounded(value, label, 0, new Set(), preserveSandboxValues)

const copyBounded = (
  value: unknown,
  label: string,
  depth: number,
  seen: Set<object>,
  preserveSandboxValues: boolean,
): unknown => {
  if (depth > MAX_VALUE_DEPTH) {
    throw new ToolRuntimeError("InvalidDataValue", `${label} exceeds the maximum value depth of ${MAX_VALUE_DEPTH}.`)
  }
  if (
    value === null ||
    value === undefined ||
    typeof value === "string" ||
    typeof value === "boolean" ||
    // NaN/Infinity are allowed to exist as in-sandbox intermediates (matching real JS and a real
    // engine) so defensive guards like `Number.isNaN(x)` / `parseInt(x) || 0` can run. They are
    // normalized to `null` when the value leaves the sandbox - see copyOut - exactly as
    // JSON.stringify already does at any tool boundary.
    typeof value === "number"
  ) {
    return value
  }

  if (typeof value !== "object") {
    throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`)
  }

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Flatten the data before passing it (e.g. JSON round-trip after removing cycles, or serialize rows instead of a nested tree).
  2. Break cycles before copying: track visited nodes and replace back-references with ids.
  3. Select only the needed subset of fields to reduce depth.
  4. Raise MAX_VALUE_DEPTH only deliberately, understanding the stack/memory cost.

Example fix

// before
tool.run({ tree: cyclicNode }) // circular/deep
// after
const safe = JSON.parse(JSON.stringify(cyclicNode, (k, v) => v === cyclicNode.parent ? null : v))
tool.run({ tree: safe })
Defensive patterns

Strategy: validation

Validate before calling

function depth(v, d = 0, seen = new Set()) {
  if (d > 16 || (typeof v === "object" && v !== null && seen.has(v))) throw new RangeError("too deep or circular")
  if (typeof v === "object" && v !== null) { seen.add(v); for (const k of Object.keys(v)) depth(v[k], d + 1, seen) }
  return v
}

Type guard

const isShallowEnough = (v, max = 16) => { try { depth(v, 0, new Set()); return true } catch { return false } }

Try / catch

try { sandboxArgs = copyIn(data) } catch (e) { if (e.code === "InvalidDataValue") sandboxArgs = copyIn(flatten(data)) }

Prevention

When it happens

Trigger: Passing a self-referential or very deeply nested object as a tool argument or return value; a linked-list-like structure built by recursive code; circular JSON-style data.

Common situations: Host tool returns a deep ORM/graph result; recursive algorithms build structures deeper than the limit; accidental circular references in cached objects.

Related errors


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