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.

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.

Appendix: 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)