{"record":{"id":"2fc6c47044322400","repo":"different-ai/openwork","slug":"switch-discriminants-must-be-data-values-in-codemo","errorCode":null,"errorMessage":"Switch discriminants must be data values in CodeMode.","messagePattern":"Switch discriminants must be data values in CodeMode\\.","errorType":"exception","errorClass":"InterpreterRuntimeError","httpStatus":null,"severity":"error","filePath":"packages/codemode/src/interpreter/runtime.ts","lineNumber":899,"sourceCode":"    const consequentNode = getNode(node, \"consequent\")\n    const alternateNode = getOptionalNode(node, \"alternate\")\n\n    return Effect.flatMap(this.evaluateExpression(testNode), (test) =>\n      test\n        ? this.evaluateStatement(consequentNode)\n        : alternateNode\n          ? this.evaluateStatement(alternateNode)\n          : Effect.succeed({ kind: \"none\" }),\n    )\n  }\n\n  private evaluateSwitchStatement(node: AstNode): Effect.Effect<StatementResult, unknown, R> {\n    const self = this\n    this.pushScope()\n    return Effect.gen(function* () {\n      const discriminant = yield* self.evaluateExpression(getNode(node, \"discriminant\"))\n      if (containsOpaqueReference(discriminant)) {\n        throw new InterpreterRuntimeError(\n          \"Switch discriminants must be data values in CodeMode.\",\n          node,\n          \"InvalidDataValue\",\n        )\n      }\n      const cases = getArray(node, \"cases\").map((value, index) => asNode(value, `cases[${index}]`))\n      let defaultIndex: number | undefined\n      let selected: number | undefined\n      for (const [index, branch] of cases.entries()) {\n        const test = getOptionalNode(branch, \"test\")\n        if (!test) {\n          defaultIndex = index\n          continue\n        }\n        const candidate = yield* self.evaluateExpression(test)\n        if (containsOpaqueReference(candidate)) {\n          throw new InterpreterRuntimeError(\n            \"Switch case values must be data values in CodeMode.\",","sourceCodeStart":881,"sourceCodeEnd":917,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/packages/codemode/src/interpreter/runtime.ts#L881-L917","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Switch on a primitive derived from the value (e.g. a name, id string, or number) instead of the reference itself.","Replace the switch with explicit if/else identity checks if reference comparison is truly needed.","Extract the data field first: `const kind = obj.kind; switch (kind) {...}`."],"exampleFix":"// before\nswitch (toolRef) {\n  case fsRead: ... }\n// after\nswitch (toolRef.name) {\n  case \"fs.read\": ... }","handlingStrategy":"validation","validationCode":"// before switch: ensure discriminant is a data primitive\nif (typeof discriminant !== \"string\" && typeof discriminant !== \"number\" && typeof discriminant !== \"boolean\") {\n  throw new Error(\"switch discriminant must be a primitive data value in CodeMode\");\n}","typeGuard":"const isDataValue = (v: unknown): boolean =>\n  v === null || [\"string\", \"number\", \"boolean\"].includes(typeof v) ||\n  (Array.isArray(v) && v.every(isDataValue)) ||\n  (v instanceof Map === false && typeof v === \"object\" && Object.values(v).every(isDataValue));","tryCatchPattern":"try {\n  interpret(src);\n} catch (e) {\n  if (e instanceof InterpreterRuntimeError && e.code === \"InvalidDataValue\") {\n    // rewrite switch to use a primitive key\n  }\n  throw e;\n}","preventionTips":["Switch only on strings/numbers/booleans in CodeMode.","Never switch on function or tool-handle values.","Extract a data field (name/id) from references before switching."],"tags":["codemode","interpreter","switch-statement","opaque-reference"],"backgroundTag":"non-serializable-value-in-sandbox","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}