pydantic/monty · error · TypeError

ClassType attrs entries must be [name, value] pairs

Error message

ClassType attrs entries must be [name, value] pairs

What it means

pushClassType in the wasm worker value codec converts a host-side ClassType marker object into a flat arena node before sending it to the sandbox. Each entry of object.attrs must be an array of exactly a name and a value. This TypeError is thrown when an attrs entry is not an array at all (e.g. an object, string, or null).

Solutions

  1. Inspect the failing attrs element and replace it with a two-element array `[name, value]`, e.g. `{ name: 'x', value: 1 }` becomes `['x', 1]`.
  2. Log the full marker object before the call to spot which attrs entry is malformed.
  3. If migrating from an object-keyed attrs form, map with `Object.entries(attrs)` which yields [name, value] pairs directly.
  4. Ensure no entry is null/undefined by validating `attrs.every(Array.isArray)` before the call.

Example fix

// before
node.attrs = [{ name: 'x', value: 1 }]
// after
node.attrs = [['x', 1]]
Defensive patterns

Strategy: validation

Validate before calling

function validClassMarker(m) {
  return m && m[TYPE_MARKER] === 'ClassType'
    && Array.isArray(m.attrs)
    && m.attrs.every(p => Array.isArray(p));
}
if (!validClassMarker(marker)) throw new TypeError('bad ClassType marker');

Type guard

const isAttrPair = (p: unknown): p is [string, unknown] =>
  Array.isArray(p) && typeof p[0] === 'string' && 1 in p;

Try / catch

try {
  await session.feedRun(code, { inputs: { cls: marker } });
} catch (e) {
  if (e instanceof TypeError && e.message.includes('ClassType attrs entries')) {
    marker.attrs = marker.attrs.filter(Array.isArray);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling a Monty value-conversion API that receives a `{ [TYPE_MARKER]: 'ClassType', attrs: [...] }` marker where some element of attrs is not an array — e.g. `attrs: [{ name: 'x', value: 1 }]`, `attrs: ['x', 1]`, or an entry of `null`.

Common situations: Hand-building ClassType markers instead of using the library's helpers; JS objects accidentally passed where tuple-style arrays are expected; refactors that changed the attrs shape from key/value objects to [name, value] pairs.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/f8c44d368688da7b. Report an issue: GitHub.

Appendix: source

Thrown at crates/monty-js/ts/worker/value.ts:221

    },
  }
}

/** Builds a class-type node from the plain `classType` marker object,
 *  appending its eager attr nodes to the arena. */
function pushClassType(
  object: Record<string, unknown>,
  nodes: ValueNode[],
): Extract<ValueNode, { tag: 'class-type' }>['val'] {
  // Require an array like the native binding does, so both transports
  // enforce the same marker contract (a missing `attrs` is a forged or
  // malformed marker, not an empty attribute list).
  if (!Array.isArray(object.attrs)) {
    throw new TypeError('ClassType attrs must be an array of [name, value] pairs')
  }
  const attrPairs: [unknown, unknown][] = []
  for (const pair of object.attrs as unknown[]) {
    if (!Array.isArray(pair)) throw new TypeError('ClassType attrs entries must be [name, value] pairs')
    if (typeof pair[0] !== 'string') throw new TypeError('ClassType attr name must be a string')
    if (!(1 in pair)) throw new TypeError('ClassType attr value missing')
    attrPairs.push([pair[0], pair[1]])
  }
  return {
    name: String(object.name),
    id: uuidString(object.id, 'ClassType id'),
    hostDefined: object.hostDefined === true,
    isDataclass: object.isDataclass === true,
    attrs: pushPairs(attrPairs, nodes),
  }
}

/** A canonical uuid string is required for identities crossing the wire. */
function uuidString(value: unknown, what: string): string {
  if (
    typeof value !== 'string' ||
    !/^[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)

View on GitHub (pinned to adc986b362)