{"record":{"id":"9ab134d445c9877d","repo":"pydantic/monty","slug":"classtype-attrs-must-be-an-array-of-name-value-pairs","errorCode":null,"errorMessage":"ClassType attrs must be an array of [name, value] pairs","messagePattern":"ClassType attrs must be an array of \\[name, value\\] pairs","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/monty-js/ts/worker/value.ts","lineNumber":217,"sourceCode":"    val: {\n      classType: classTypeIndex,\n      instanceId: uuidString(object.instanceId, 'ClassInstance instanceId'),\n      attrs: pushPairs(pairs, nodes),\n    },\n  }\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. */","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/ts/worker/value.ts#L199-L235","documentation":"pushClassType requires a ClassType marker's `attrs` to be an array of [name, value] pairs. A missing or non-array attrs is treated as a forged or malformed marker (not an empty class) and rejected with this TypeError, matching the native binding's contract.","triggerScenarios":"Passing a ClassType marker without an attrs key; attrs as an object map instead of an array of pairs; markers reconstructed from partial data where class attributes were dropped.","commonSituations":"Hand-building class type descriptors for typed inputs; trimming marker payloads and accidentally removing attrs; converting markers from a format where empty attrs is null.","solutions":["Include attrs: [] explicitly even when the class has no attributes","Ensure attrs is an array of [stringName, value] pairs","Validate with Array.isArray(marker.attrs) before passing the marker"],"exampleFix":"// before\n{ marker: 'ClassType', name: 'Empty' } // attrs missing\n// after\n{ marker: 'ClassType', name: 'Empty', attrs: [] }","handlingStrategy":"validation","validationCode":"const assertClassType = (o: Record<string, unknown>): void => {\n  if (!Array.isArray(o.attrs)) throw new Error('ClassType.attrs must be an array of [name, value] pairs')\n  if (!o.attrs.every(p => Array.isArray(p) && typeof p[0] === 'string' && 1 in p)) throw new Error('Bad ClassType attrs pair')\n}","typeGuard":"const isClassType = (v: unknown): v is { name: string; attrs: [string, unknown][] } =>\n  typeof v === 'object' && v !== null && typeof (v as any).name === 'string' && Array.isArray((v as any).attrs)","tryCatchPattern":"try {\n  return pushClassType(object, nodes)\n} catch (e) {\n  if (e instanceof TypeError && e.message.startsWith('ClassType attrs must be an array')) {\n    throw new Error('Forged or incomplete ClassType marker: attrs array required')\n  }\n  throw e\n}","preventionTips":["Always include attrs (use [] for attribute-less classes), never omit the key","Keep markers in their runtime-produced shape; don't trim fields for payload size","Validate marker shape once at your transport boundary before calling the worker API"],"tags":["typescript","validation","class-type","value-conversion"],"backgroundTag":"schema-validation-failed","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"}