{"record":{"id":"bcd6db7574e56186","repo":"garrytan/gstack","slug":"shard-count-must-be-a-positive-integer-received","errorCode":null,"errorMessage":"Shard count must be a positive integer. Received: ${shardCount}","messagePattern":"Shard count must be a positive integer\\. Received: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/test-free-shards.ts","lineNumber":208,"sourceCode":"    } else {\n      safe.push(relativePath);\n    }\n  }\n  return { safe, excluded };\n}\n\nexport function stableHash(input: string): number {\n  let hash = 0x811c9dc5;\n  for (let index = 0; index < input.length; index += 1) {\n    hash ^= input.charCodeAt(index);\n    hash = Math.imul(hash, 0x01000193);\n  }\n  return hash >>> 0;\n}\n\nexport function assignFilesToShards(files: string[], shardCount: number): string[][] {\n  if (!Number.isInteger(shardCount) || shardCount <= 0) {\n    throw new Error(`Shard count must be a positive integer. Received: ${shardCount}`);\n  }\n\n  const shards = Array.from({ length: shardCount }, () => [] as string[]);\n  for (const file of files) {\n    const shardIndex = stableHash(file) % shardCount;\n    shards[shardIndex].push(file);\n  }\n\n  return shards\n    .map(filesInShard => filesInShard.sort())\n    .filter(filesInShard => filesInShard.length > 0);\n}\n\nexport function buildShardArgs(files: string[]): string[] {\n  return ['test', ...files, '--max-concurrency=1', `--timeout=${FREE_TEST_TIMEOUT_MS}`];\n}\n\ntype CliOptions = {","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/scripts/test-free-shards.ts#L190-L226","documentation":"assignFilesToShards() in scripts/test-free-shards.ts:208 requires shardCount to be a positive integer. Zero, negatives, non-integers, and NaN all throw. The function then builds N empty shards and distributes files via stableHash modulo shardCount.","triggerScenarios":"Passing 0 (e.g. when file set is tiny). Passing a float from a division. Passing NaN from Number.parseInt('abc'). Negative shard count.","commonSituations":"Reading shard count from an env var without validation. Math that yields 0 for small inputs. Unset CLI flag parsed to NaN.","solutions":["Validate the input is a positive integer before calling","Clamp to at least 1 when deriving from file count","Default to DEFAULT_SHARD_COUNT when the value is unset or invalid"],"exampleFix":"// before\nassignFilesToShards(files, 0)\n// after\nassignFilesToShards(files, DEFAULT_SHARD_COUNT)","handlingStrategy":"type-guard","validationCode":"if (!Number.isInteger(shardCount) || shardCount <= 0) {\n  throw new Error(`Shard count must be a positive integer. Received: ${shardCount}`);\n}","typeGuard":"const isPositiveInteger = (n: number): boolean => Number.isInteger(n) && n > 0;","tryCatchPattern":null,"preventionTips":["Parse and validate CLI-supplied numbers before passing them in","Reject empty file lists early so derived shard counts never hit 0","Default to DEFAULT_SHARD_COUNT when the value is unset or invalid"],"tags":["test","sharding","validation"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}