{"record":{"id":"27e7cf9dae6acff2","repo":"can1357/oh-my-pi","slug":"number-max-must-not-be-nan","errorCode":null,"errorMessage":"number max must not be NaN","messagePattern":"number max must not be NaN","errorType":"exception","errorClass":"OmpTypeError","httpStatus":null,"severity":"error","filePath":"packages/omptype/src/zod.ts","lineNumber":153,"sourceCode":"\t\t\t\tconst min = ir.min === undefined ? bound : Math.max(ir.min, bound);\n\t\t\t\treturn next(restrictBase(schema, { ...ir, min }));\n\t\t\t}\n\t\t\tif (ir.k === \"number\") {\n\t\t\t\tif (Number.isNaN(bound)) throw new OmpTypeError(\"number min must not be NaN\");\n\t\t\t\tif (ir.min !== undefined && ir.min >= bound) return next(restrictBase(schema, ir));\n\t\t\t\treturn next(restrictBase(schema, { ...ir, min: bound, xmin: false }));\n\t\t\t}\n\t\t\tthrow new OmpTypeError(`cannot apply min to ${ir.k}`);\n\t\t},\n\t\tmax(bound: number): ZodLikeSchema<Out> {\n\t\t\tconst ir = schema.ir;\n\t\t\tif (ir.k === \"string\" || ir.k === \"array\") {\n\t\t\t\tlengthBound(\"max\", schema, bound);\n\t\t\t\tconst max = ir.max === undefined ? bound : Math.min(ir.max, bound);\n\t\t\t\treturn next(restrictBase(schema, { ...ir, max }));\n\t\t\t}\n\t\t\tif (ir.k === \"number\") {\n\t\t\t\tif (Number.isNaN(bound)) throw new OmpTypeError(\"number max must not be NaN\");\n\t\t\t\tif (ir.max !== undefined && ir.max <= bound) return next(restrictBase(schema, ir));\n\t\t\t\treturn next(restrictBase(schema, { ...ir, max: bound, xmax: false }));\n\t\t\t}\n\t\t\tthrow new OmpTypeError(`cannot apply max to ${ir.k}`);\n\t\t},\n\t\tint(): ZodLikeSchema<Out> {\n\t\t\tif (schema.ir.k !== \"number\") throw new OmpTypeError(`cannot apply int to ${schema.ir.k}`);\n\t\t\treturn next(restrictBase(schema, { ...schema.ir, int: true }));\n\t\t},\n\t\tpositive(): ZodLikeSchema<Out> {\n\t\t\tif (schema.ir.k !== \"number\") throw new OmpTypeError(`cannot apply positive to ${schema.ir.k}`);\n\t\t\tconst ir = schema.ir;\n\t\t\tif (ir.min !== undefined && ir.min > 0) return next(restrictBase(schema, ir));\n\t\t\treturn next(restrictBase(schema, { ...ir, min: 0, xmin: true }));\n\t\t},\n\t\tnonnegative(): ZodLikeSchema<Out> {\n\t\t\tif (schema.ir.k !== \"number\") throw new OmpTypeError(`cannot apply nonnegative to ${schema.ir.k}`);\n\t\t\treturn this.min(0);","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/omptype/src/zod.ts#L135-L171","documentation":"omptype's Zod-like `.max()` on a number schema rejects NaN bounds. A NaN bound would make every comparison meaningless and silently invalidate all values, so the library throws an OmpTypeError at schema-construction time instead of failing at validation time. This is a fail-fast guard in `max()` at packages/omptype/src/zod.ts:153.","triggerScenarios":"Calling `.max(bound)` on a number schema where `Number.isNaN(bound)` is true — typically `.max(someNumber)` where someNumber came from a failed parse (parseFloat/Number of undefined, empty string, or missing config).","commonSituations":"Config values read from env or JSON that are undefined or non-numeric, then passed to Number()/parseFloat() yielding NaN; arithmetic on missing fields; data-driven bounds from a file with a blank cell.","solutions":["Check the bound with Number.isNaN() before calling .max() and use a sane fallback","Trace where the bound value is computed; fix the parse/lookup that produced NaN","If 'no limit' is intended, skip calling .max() entirely instead of passing NaN"],"exampleFix":"// before\nconst limit = Number(process.env.MAX_ITEMS);\nschema.max(limit); // throws when MAX_ITEMS is undefined\n// after\nconst parsed = Number(process.env.MAX_ITEMS);\nconst limit = Number.isNaN(parsed) ? 100 : parsed;\nschema.max(limit);","handlingStrategy":"validation","validationCode":"function assertFiniteBound(bound: number, name = \"max\"): void {\n  if (!Number.isFinite(bound)) throw new Error(`${name} bound must be finite, got ${bound}`);\n}\nassertFiniteBound(limit); // before schema.max(limit)","typeGuard":"function isFiniteNumber(v: unknown): v is number {\n  return typeof v === \"number\" && Number.isFinite(v);\n}","tryCatchPattern":"try {\n  schema = base.max(bound);\n} catch (err) {\n  if (err instanceof OmpTypeError && /NaN/.test(err.message)) {\n    schema = base; // skip the bound and log the bad input\n  } else throw err;\n}","preventionTips":["Never pass raw Number(envVar)/parseFloat results straight into .max(); run them through Number.isFinite()","Give config-derived bounds explicit defaults with a validate step","Prefer omitting the .max() call over passing a sentinel like NaN or Infinity"],"tags":["schema-builder","nan","number","validation"],"backgroundTag":"schema-bound-is-nan","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}