{"record":{"id":"ecf12608ff927c17","repo":"different-ai/openwork","slug":"expected-key-to-be-a-string","errorCode":null,"errorMessage":"Expected '${key}' to be a string.","messagePattern":"Expected '(.+?)' to be a string\\.","errorType":"exception","errorClass":"InterpreterRuntimeError","httpStatus":null,"severity":"error","filePath":"packages/codemode/src/interpreter/model.ts","lineNumber":174,"sourceCode":"export const isRecord = (value: unknown): value is Record<string, unknown> =>\n  typeof value === \"object\" && value !== null\n\nexport const asNode = (value: unknown, context: string): AstNode => {\n  if (!isRecord(value) || typeof value.type !== \"string\") {\n    throw new InterpreterRuntimeError(`Invalid AST node while reading ${context}.`)\n  }\n  return value as AstNode\n}\n\nexport const getArray = (node: AstNode, key: string): Array<unknown> => {\n  const value = node[key]\n  if (!Array.isArray(value)) throw new InterpreterRuntimeError(`Expected '${key}' to be an array.`, node)\n  return value\n}\n\nexport const getString = (node: AstNode, key: string): string => {\n  const value = node[key]\n  if (typeof value !== \"string\") throw new InterpreterRuntimeError(`Expected '${key}' to be a string.`, node)\n  return value\n}\n\nexport const getBoolean = (node: AstNode, key: string): boolean => {\n  const value = node[key]\n  if (typeof value !== \"boolean\") throw new InterpreterRuntimeError(`Expected '${key}' to be a boolean.`, node)\n  return value\n}\n\nexport const getOptionalNode = (node: AstNode, key: string): AstNode | undefined => {\n  const value = node[key]\n  if (value === undefined || value === null) return undefined\n  return asNode(value, key)\n}\n\nexport const getNode = (node: AstNode, key: string): AstNode => asNode(node[key], key)\n\nexport const sourceLocation = (node: AstNode): { readonly line: number; readonly column: number } => ({","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/packages/codemode/src/interpreter/model.ts#L156-L192","documentation":"getString reads a property off an AstNode and asserts it is a string, throwing InterpreterRuntimeError (node attached) otherwise. It guards literal values, identifiers, operator names, etc., so a non-string (undefined, number, object) means the AST node lacks or misnames the expected field.","triggerScenarios":"Calling getString(node, key) where node[key] is undefined (missing/typo'd key), a number (numeric literal stored as number), or another non-string type — e.g. getString(callNode, \"callee\") on a node whose callee is a nested node, not a name.","commonSituations":"Grammar mismatch between parser and interpreter versions; expecting an identifier name where the parser stores a nested Identifier node; accessing a key absent on that node kind (e.g. reading `name` off a node type that has no name).","solutions":["Check the node's actual shape (the error attaches the node) and use the correct key","If the value is a nested node, retrieve it with getNode/asNode and read its string field instead","Match parser and interpreter versions so field encodings agree","Use getOptional-style access or check key existence for optional string fields"],"exampleFix":"// before\nconst name = getString(callNode, \"callee\") // callee is a node\n// after\nconst name = getString(asNode(callNode.callee, \"callee\"), \"name\")","handlingStrategy":"type-guard","validationCode":"const v = node[key]\nif (typeof v !== \"string\") {\n  throw new Error(`Expected string at node.${key}, got ${typeof v}`)\n}\ngetString(node, key)","typeGuard":"const hasStringField = (node: AstNode, key: string): boolean => typeof node[key] === \"string\"","tryCatchPattern":"try {\n  name = getString(node, \"name\")\n} catch (e) {\n  if (e instanceof InterpreterRuntimeError && e.message.includes(\"to be a string\")) {\n    // value may be a nested Identifier node; resolve its name instead\n    name = node[\"name\"] && typeof node[\"name\"] === \"object\" ? getString(node[\"name\"] as AstNode, \"name\") : null\n  } else throw e\n}","preventionTips":["Read nested identifiers via getNode + getString('name') instead of expecting a flat string","Check node kind before reading kind-specific string fields","Keep parser/interpreter versions aligned so field encodings agree","Log the attached node on failure — it names the exact construct that diverged"],"tags":["ast","interpreter","type-mismatch"],"backgroundTag":"malformed-ast-node","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}