different-ai/openwork · error · InterpreterRuntimeError

Switch discriminants must be data values in CodeMode.

Error message

Switch discriminants must be data values in CodeMode.

What it means

The CodeMode interpreter only allows pure data values in a switch discriminant. If evaluateSwitchStatement detects the evaluated discriminant contains an opaque reference (a live tool handle, function, or other non-serializable value tracked by containsOpaqueReference), it refuses to run the switch because reference identity matching against case values is not meaningful/safe in the sandbox. Throw as InvalidDataValue at the switch node.

Source

Thrown at packages/codemode/src/interpreter/runtime.ts:899

    const consequentNode = getNode(node, "consequent")
    const alternateNode = getOptionalNode(node, "alternate")

    return Effect.flatMap(this.evaluateExpression(testNode), (test) =>
      test
        ? this.evaluateStatement(consequentNode)
        : alternateNode
          ? this.evaluateStatement(alternateNode)
          : Effect.succeed({ kind: "none" }),
    )
  }

  private evaluateSwitchStatement(node: AstNode): Effect.Effect<StatementResult, unknown, R> {
    const self = this
    this.pushScope()
    return Effect.gen(function* () {
      const discriminant = yield* self.evaluateExpression(getNode(node, "discriminant"))
      if (containsOpaqueReference(discriminant)) {
        throw new InterpreterRuntimeError(
          "Switch discriminants must be data values in CodeMode.",
          node,
          "InvalidDataValue",
        )
      }
      const cases = getArray(node, "cases").map((value, index) => asNode(value, `cases[${index}]`))
      let defaultIndex: number | undefined
      let selected: number | undefined
      for (const [index, branch] of cases.entries()) {
        const test = getOptionalNode(branch, "test")
        if (!test) {
          defaultIndex = index
          continue
        }
        const candidate = yield* self.evaluateExpression(test)
        if (containsOpaqueReference(candidate)) {
          throw new InterpreterRuntimeError(
            "Switch case values must be data values in CodeMode.",

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Switch on a primitive derived from the value (e.g. a name, id string, or number) instead of the reference itself.
  2. Replace the switch with explicit if/else identity checks if reference comparison is truly needed.
  3. Extract the data field first: `const kind = obj.kind; switch (kind) {...}`.

Example fix

// before
switch (toolRef) {
  case fsRead: ... }
// after
switch (toolRef.name) {
  case "fs.read": ... }
Defensive patterns

Strategy: validation

Validate before calling

// before switch: ensure discriminant is a data primitive
if (typeof discriminant !== "string" && typeof discriminant !== "number" && typeof discriminant !== "boolean") {
  throw new Error("switch discriminant must be a primitive data value in CodeMode");
}

Type guard

const isDataValue = (v: unknown): boolean =>
  v === null || ["string", "number", "boolean"].includes(typeof v) ||
  (Array.isArray(v) && v.every(isDataValue)) ||
  (v instanceof Map === false && typeof v === "object" && Object.values(v).every(isDataValue));

Try / catch

try {
  interpret(src);
} catch (e) {
  if (e instanceof InterpreterRuntimeError && e.code === "InvalidDataValue") {
    // rewrite switch to use a primitive key
  }
  throw e;
}

Prevention

When it happens

Trigger: A `switch (x)` statement where x evaluates to a value containing an opaque reference: e.g. switching on a tool handle returned by the tools namespace, a function value, or an object/array nested-containing such a reference.

Common situations: Writing `switch (tool)` or `switch (obj.callback)` in CodeMode scripts generated by an LLM, expecting plain JS semantics where switching on any value is legal; the sandboxed CodeMode dialect forbids it.

Related errors


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