{"record":{"id":"b90b1cea15ea69bf","repo":"pydantic/monty","slug":"classtype-attr-name-must-be-a-string","errorCode":null,"errorMessage":"ClassType attr name must be a string","messagePattern":"ClassType attr name must be a string","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/monty-js/ts/worker/value.ts","lineNumber":222,"sourceCode":"  }\n}\n\n/** Builds a class-type node from the plain `classType` marker object,\n *  appending its eager attr nodes to the arena. */\nfunction pushClassType(\n  object: Record<string, unknown>,\n  nodes: ValueNode[],\n): Extract<ValueNode, { tag: 'class-type' }>['val'] {\n  // Require an array like the native binding does, so both transports\n  // enforce the same marker contract (a missing `attrs` is a forged or\n  // malformed marker, not an empty attribute list).\n  if (!Array.isArray(object.attrs)) {\n    throw new TypeError('ClassType attrs must be an array of [name, value] pairs')\n  }\n  const attrPairs: [unknown, unknown][] = []\n  for (const pair of object.attrs as unknown[]) {\n    if (!Array.isArray(pair)) throw new TypeError('ClassType attrs entries must be [name, value] pairs')\n    if (typeof pair[0] !== 'string') throw new TypeError('ClassType attr name must be a string')\n    if (!(1 in pair)) throw new TypeError('ClassType attr value missing')\n    attrPairs.push([pair[0], pair[1]])\n  }\n  return {\n    name: String(object.name),\n    id: uuidString(object.id, 'ClassType id'),\n    hostDefined: object.hostDefined === true,\n    isDataclass: object.isDataclass === true,\n    attrs: pushPairs(attrPairs, nodes),\n  }\n}\n\n/** A canonical uuid string is required for identities crossing the wire. */\nfunction uuidString(value: unknown, what: string): string {\n  if (\n    typeof value !== 'string' ||\n    !/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(value)\n  ) {","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/ts/worker/value.ts#L204-L240","documentation":"When decoding a ClassType marker, every attrs entry must be a [name, value] pair whose first element (the attribute name) is a string. This TypeError signals that pair[0] is not a string — for example a number, symbol, or undefined. Attribute names become Python identifiers on the sandbox side, so they must be strings.","triggerScenarios":"Passing a ClassType marker whose attrs contain a pair with a non-string first element, e.g. `attrs: [[42, 'v']]`, `attrs: [[undefined, 1]]`, or a sparse array where index 0 is unset. Also triggered by numeric-like names not wrapped as strings: `[[1, 'x']]`.","commonSituations":"Generating attrs programmatically from Object.entries of a Map with non-string keys; forgetting to stringify names coming from user input or config; JS Symbol keys leaking into attrs.","solutions":["Coerce the name explicitly: `attrs: [[String(name), value], ...]` or template-literal it.","Validate before the call: `attrs.every(p => Array.isArray(p) && typeof p[0] === 'string')`.","If names come from a Map with non-string keys, convert keys to strings when building pairs.","Check for sparse arrays created via `new Array(n)` and holes at index 0."],"exampleFix":"// before\nattrs: [[42, 'value']]\n// after\nattrs: [['answer', 'value']]","handlingStrategy":"type-guard","validationCode":"const bad = marker.attrs.filter(p => !Array.isArray(p) || typeof p[0] !== 'string');\nif (bad.length) throw new TypeError(`non-string attr names: ${JSON.stringify(bad)}`);","typeGuard":"const isStringNamedPair = (p: unknown): p is [string, unknown] =>\n  Array.isArray(p) && typeof p[0] === 'string';","tryCatchPattern":"try {\n  await session.feedRun(code, { inputs: { cls: marker } });\n} catch (e) {\n  if (e instanceof TypeError && e.message === 'ClassType attr name must be a string') {\n    marker.attrs = marker.attrs.map(([n, v]) => [String(n), v]);\n  } else throw e;\n}","preventionTips":["Coerce names with String(name) when they originate outside your code","Avoid Symbol or numeric keys anywhere near attrs construction","Validate attrs names are strings before calling the API","Keep attrs construction in one helper so the invariant is enforced once"],"tags":["typescript","wasm","value-conversion","typeerror"],"backgroundTag":"type-mismatch","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}