{"record":{"id":"b5a3d8f6cfc3dd6a","repo":"tinyhumansai/openhuman","slug":"label-must-be-a-non-negative-integer","errorCode":null,"errorMessage":"${label} must be a non-negative integer","messagePattern":"(.+?) must be a non-negative integer","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/debug/harness-cache-audit.mjs","lineNumber":145,"sourceCode":"        process.exit(0);\n      default:\n        throw new Error(`unknown option: ${arg}`);\n    }\n  }\n  return opts;\n}\n\nfunction parsePositiveInt(raw, label) {\n  const value = Number(raw);\n  if (!Number.isInteger(value) || value < 1)\n    throw new Error(`${label} must be a positive integer`);\n  return value;\n}\n\nfunction parseNonNegativeInt(raw, label) {\n  const value = Number(raw);\n  if (!Number.isInteger(value) || value < 0)\n    throw new Error(`${label} must be a non-negative integer`);\n  return value;\n}\n\nfunction parseNonNegativeNumber(raw, label) {\n  const value = Number(raw);\n  if (!Number.isFinite(value) || value < 0)\n    throw new Error(`${label} must be a non-negative number`);\n  return value;\n}\n\nfunction defaultOpenhumanDir() {\n  return process.env.OPENHUMAN_APP_ENV === \"staging\"\n    ? path.join(homedir(), \".openhuman-staging\")\n    : path.join(homedir(), \".openhuman\");\n}\n\nasync function defaultWorkspace() {\n  if (process.env.OPENHUMAN_WORKSPACE) return process.env.OPENHUMAN_WORKSPACE;","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/debug/harness-cache-audit.mjs#L127-L163","documentation":"parseNonNegativeInt() requires Number(raw) to be an integer >= 0 and throws with the label otherwise. In harness-cache-audit.mjs it guards --max-turns-without-cache, the audit failure threshold for how many completed turns may have zero cached input. Zero is allowed here (strictest setting), unlike the positive-int guard — the two validators are easy to confuse.","triggerScenarios":"`--max-turns-without-cache -1` (negative), `--max-turns-without-cache 1.5` (fractional), `--max-turns-without-cache many` (NaN), or a shell arithmetic expansion that failed and produced an empty/non-numeric token.","commonSituations":"Trying to express 'fail on any uncached turn' with -1 instead of 0; passing a percentage or a ratio where a count is expected; copy-paste from a notes file with a unit suffix (\"2 turns\").","solutions":["Use a whole number >= 0: `--max-turns-without-cache 0` means every turn must hit cache","Strip units/quotes from the value","Validate in the wrapper: `[[ $MAX =~ ^[0-9]+$ ]] || exit 1` before invoking"],"exampleFix":"# before\nnode scripts/debug/harness-cache-audit.mjs --max-turns-without-cache -1\n# Error: --max-turns-without-cache must be a non-negative integer\n\n# after\nnode scripts/debug/harness-cache-audit.mjs --max-turns-without-cache 0","handlingStrategy":"validation","validationCode":"const toNonNegativeInt = (raw, label) => {\n  const n = Number(raw);\n  if (!Number.isInteger(n) || n < 0) {\n    console.error(`${label} must be a whole number >= 0 (got: ${raw})`);\n    process.exit(2);\n  }\n  return n;\n};\nconst maxUncached = toNonNegativeInt(process.env.MAX_UNCACHED ?? \"1\", \"--max-turns-without-cache\");","typeGuard":"const isNonNegativeIntString = (s) => /^\\d+$/.test(String(s).trim());","tryCatchPattern":null,"preventionTips":["Express 'fail on any uncached turn' as 0, never -1","Validate wrapper-provided values with a ^\\d+$ regex before composing the command line"],"tags":["cli","validation","numeric-parsing"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}