{"record":{"id":"7c5c1fdf41878cc1","repo":"pydantic/monty","slug":"monty-typename-timezonename-requires-offsetseconds","errorCode":null,"errorMessage":"Monty${typeName} timezoneName requires offsetSeconds","messagePattern":"Monty(.+?) timezoneName requires offsetSeconds","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/monty-js/ts/worker/value.ts","lineNumber":160,"sourceCode":"      if (typeof object.classType === 'object' && object.classType !== null) {\n        return { tag: 'class-type', val: pushClassType(object.classType as Record<string, unknown>, nodes) }\n      }\n      return { tag: 'type-name', val: String(object.value) }\n    case 'BuiltinFunction':\n      return { tag: 'builtin-function', val: String(object.value) }\n    default:\n      throw new TypeError(`Unknown Monty marker type: ${String(object[TYPE_MARKER])}`)\n  }\n}\n\n/** Preserves aware-time metadata while rejecting an orphaned timezone name. */\nfunction timeZoneFields(\n  object: Record<string, unknown>,\n  typeName: 'DateTime' | 'Time',\n): { offsetSeconds?: number; timezoneName?: string } {\n  const aware = object.offsetSeconds !== undefined && object.offsetSeconds !== null\n  if (!aware && object.timezoneName !== undefined && object.timezoneName !== null) {\n    throw new TypeError(`Monty${typeName} timezoneName requires offsetSeconds`)\n  }\n  return aware\n    ? {\n        offsetSeconds: Number(object.offsetSeconds),\n        ...(typeof object.timezoneName === 'string' ? { timezoneName: object.timezoneName } : {}),\n      }\n    : {}\n}\n\n/**\n * Validates and converts a host `ClassInstance` marker (same shape the napi\n * path produces: `attrs` as ordered `[name, value]` pairs, uuids as\n * strings). Validation messages mirror napi's so both transports fail\n * malformed markers alike.\n */\nfunction pushClassInstance(object: Record<string, unknown>, nodes: ValueNode[]): ValueNode {\n  if (typeof object.type !== 'object' || object.type === null) {\n    throw new TypeError(","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/ts/worker/value.ts#L142-L178","documentation":"timeZoneFields validates marker objects for DateTime/Time values: a timezone name may only appear together with an offsetSeconds (i.e. the value must be tz-aware). A timezoneName without offsetSeconds is considered an orphaned/malformed marker and rejected with this TypeError.","triggerScenarios":"Supplying a MontyDateTime or MontyTime marker object where offsetSeconds is undefined/null but timezoneName is a non-empty string — e.g. hand-constructing an aware datetime input, or dropping the offsetSeconds field while keeping timezoneName when mapping values between systems.","commonSituations":"Building datetime inputs for a monty session by hand and forgetting offsetSeconds; a custom serializer that omits undefined fields (e.g. JSON round-trip dropping undefined) leaving timezoneName behind; naive/aware confusion when bridging host date objects.","solutions":["Include a numeric offsetSeconds whenever timezoneName is set","If the time is naive, remove timezoneName entirely instead of sending it without an offset","Check any JSON serialization step so undefined offsetSeconds does not silently drop the pairing field"],"exampleFix":"// before\n{ marker: 'DateTime', year: 2026, month: 9, day: 13, timezoneName: 'Europe/Berlin' }\n// after\n{ marker: 'DateTime', year: 2026, month: 9, day: 13, offsetSeconds: 7200, timezoneName: 'Europe/Berlin' }","handlingStrategy":"validation","validationCode":"function validateTimeFields(o: Record<string, unknown>): void {\n  const aware = o.offsetSeconds !== undefined && o.offsetSeconds !== null\n  if (!aware && o.timezoneName != null) throw new Error('timezoneName requires offsetSeconds')\n}","typeGuard":"const isConsistentTime = (o: Record<string, unknown>): boolean =>\n  !((o.offsetSeconds === undefined || o.offsetSeconds === null) && o.timezoneName != null)","tryCatchPattern":"try {\n  await session.feedRun(code, { inputs: { when: dateTimeMarker } })\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('timezoneName requires offsetSeconds')) {\n    // repair or drop the timezone name, then retry\n  }\n  throw e\n}","preventionTips":["Always pair timezoneName with a numeric offsetSeconds","Beware serializers that drop undefined offsetSeconds fields on JSON round-trips","Distinguish naive and aware datetimes explicitly in host code"],"tags":["typescript","datetime","timezone","validation"],"backgroundTag":"missing-required-argument","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"}