{"record":{"id":"725516078d41af9c","repo":"pydantic/monty","slug":"montyfilehandle-position-must-be-a-non-negative-safe-integer","errorCode":null,"errorMessage":"MontyFileHandle position must be a non-negative safe integer","messagePattern":"MontyFileHandle position must be a non-negative safe integer","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/monty-js/ts/types.ts","lineNumber":156,"sourceCode":"      if (text) throw new TypeError('invalid mode: text mode specified twice')\n      text = true\n    } else if (char === '+') {\n      throw new TypeError(\"update modes ('+') are not yet supported\")\n    } else {\n      throw new TypeError(`invalid mode: '${char}'`)\n    }\n  }\n  if (binary && text) throw new TypeError(\"can't have text and binary mode at once\")\n  if (action === undefined) {\n    throw new TypeError('Must have exactly one of create/read/write/append mode and at most one plus')\n  }\n  return `${action}${binary ? 'b' : ''}`\n}\n\n/** Validates that a file position can cross the JavaScript boundary exactly. */\nexport function validateFilePosition(position: unknown): asserts position is number {\n  if (typeof position !== 'number' || !Number.isSafeInteger(position) || position < 0) {\n    throw new TypeError('MontyFileHandle position must be a non-negative safe integer')\n  }\n}\n","sourceCodeStart":138,"sourceCodeEnd":159,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/ts/types.ts#L138-L159","documentation":"The position passed to a MontyFileHandle must be a JavaScript number that is a non-negative safe integer (<= Number.MAX_SAFE_INTEGER) so it crosses the JS/WASM boundary exactly. validateFilePosition throws this TypeError for anything else — non-numbers, NaN, negatives, floats, or integers above 2^53-1.","triggerScenarios":"Calling MontyFileHandle's constructor or pushFileHandle with position = -1, 1.5, '10' (string), NaN, or a 64-bit offset from a host file larger than 2^53 bytes.","commonSituations":"Reading a seek offset from config/JSON where it arrives as a string; computing offsets with arithmetic that yields floats; files whose sizes exceed safe-integer range (rare but possible with large host files).","solutions":["Ensure the value is a non-negative integer before passing: `Math.max(0, Math.trunc(offset))`","Convert string values with `Number(str)` and validate with Number.isSafeInteger","For offsets beyond Number.MAX_SAFE_INTEGER, the binding cannot represent them — restructure to avoid such offsets or track them in chunks"],"exampleFix":"// before\nfh.seek(BigInt(offset))\n// after\nconst pos = Number(offset)\nif (!Number.isSafeInteger(pos) || pos < 0) throw new Error('bad offset')\nfh.seek(pos)","handlingStrategy":"type-guard","validationCode":"function isValidPosition(p) { return typeof p === 'number' && Number.isSafeInteger(p) && p >= 0 }","typeGuard":"function isFilePosition(p) { return typeof p === 'number' && Number.isSafeInteger(p) && p >= 0 }","tryCatchPattern":"try { fh.seek(pos) } catch (e) { if (e instanceof TypeError && e.message.includes('non-negative safe integer')) throw new Error(`bad file position: ${String(pos)}`) ; throw e }","preventionTips":["Coerce string offsets with Number() before use","Use Math.trunc to eliminate accidental floats","Keep file offsets below Number.MAX_SAFE_INTEGER; chunk larger files","Never pass BigInt directly — the binding expects a JS number"],"tags":["typescript","type-safety","file-position"],"backgroundTag":"invalid-argument-value","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"}