{"record":{"id":"e5dd5ec18e9c2db7","repo":"tobi/qmd","slug":"name-must-be-a-positive-integer-e5dd5e","errorCode":null,"errorMessage":"${name} must be a positive integer","messagePattern":"(.+?) must be a positive integer","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/store.ts","lineNumber":1800,"sourceCode":"  body: string;\n};\n\ntype ChunkItem = {\n  hash: string;\n  path: string;\n  title: string;\n  text: string;\n  seq: number;\n  pos: number;\n  tokens: number;\n  bytes: number;\n  expectedTotalChunks: number;\n};\n\nfunction validatePositiveIntegerOption(name: string, value: number | undefined, fallback: number): number {\n  if (value === undefined) return fallback;\n  if (!Number.isInteger(value) || value < 1) {\n    throw new Error(`${name} must be a positive integer`);\n  }\n  return value;\n}\n\nfunction resolveEmbedOptions(options?: EmbedOptions): Required<Pick<EmbedOptions, \"maxDocsPerBatch\" | \"maxBatchBytes\">> {\n  return {\n    maxDocsPerBatch: validatePositiveIntegerOption(\"maxDocsPerBatch\", options?.maxDocsPerBatch, DEFAULT_EMBED_MAX_DOCS_PER_BATCH),\n    maxBatchBytes: validatePositiveIntegerOption(\"maxBatchBytes\", options?.maxBatchBytes, DEFAULT_EMBED_MAX_BATCH_BYTES),\n  };\n}\n\nconst CONTENT_VECTOR_DESIRED_COLUMNS: { name: string; definition: string }[] = [\n  { name: \"seq\", definition: \"INTEGER NOT NULL DEFAULT 0\" },\n  { name: \"pos\", definition: \"INTEGER NOT NULL DEFAULT 0\" },\n  { name: \"model\", definition: \"TEXT NOT NULL DEFAULT ''\" },\n  { name: \"embed_fingerprint\", definition: \"TEXT NOT NULL DEFAULT ''\" },\n  { name: \"total_chunks\", definition: \"INTEGER NOT NULL DEFAULT 1\" },\n  { name: \"embedded_at\", definition: \"TEXT NOT NULL DEFAULT ''\" },","sourceCodeStart":1782,"sourceCodeEnd":1818,"githubUrl":"https://github.com/tobi/qmd/blob/dbfd0b4736aeaf761d1a16ca8e424f071df8feb9/src/store.ts#L1782-L1818","documentation":"resolveEmbedOptions() validates numeric embedding options (maxDocsPerBatch, maxBatchBytes) via validatePositiveIntegerOption, which rejects any value that is not an integer >= 1. Undefined falls back to the default, but 0, negatives, floats, NaN, or strings that coerce incorrectly throw.","triggerScenarios":"Passing embed options like { maxDocsPerBatch: 0 } or { maxBatchBytes: 1.5 } to the embedding pipeline that calls resolveEmbedOptions.","commonSituations":"Config/env overrides parsed as 0 by mistake; computing a batch size dynamically and getting 0 on empty input; passing a float from a ratio calculation.","solutions":["Pass a positive integer >= 1 or omit the option to use the default","Fix the computation producing 0/float/NaN before calling embed","Clamp: Math.max(1, Math.floor(value))"],"exampleFix":"// before\nembed(docs, { maxDocsPerBatch: 0 });\n// after\nembed(docs, { maxDocsPerBatch: Math.max(1, Math.floor(opts.batch || DEFAULT)) });","handlingStrategy":"validation","validationCode":"const safe = (v?: number, fb: number) => v == null ? fb : (Number.isInteger(v) && v >= 1 ? v : undefined!); // validate before pass","typeGuard":"const isValidBatchOpt = (v: unknown): v is number => typeof v === 'number' && Number.isInteger(v) && v >= 1;","tryCatchPattern":null,"preventionTips":["Clamp computed options: Math.max(1, Math.floor(x))","Reject 0/NaN early at config parse time","Omit options to use defaults when unsure"],"tags":["validation","options","embedding"],"backgroundTag":"invalid-option-value","analyzedSha":"dbfd0b4736aeaf761d1a16ca8e424f071df8feb9","analyzedAt":"2026-08-28T18:07:46.628Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}