{"record":{"id":"0045097ade6d8beb","repo":"affaan-m/ECC","slug":"invalid-issuenumber-expected-positive-integer-go","errorCode":null,"errorMessage":"invalid issueNumber: expected positive integer, got ${JSON.stringify(issueNumber)}","messagePattern":"invalid issueNumber: expected positive integer, got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/github-coordination/actions.js","lineNumber":27,"sourceCode":"  buildIssueStateFromAction,\n  desiredLabelsForState,\n  getCoordinationState,\n  summarizeStateForOutput,\n  syncIssueLabels,\n  verifyDependenciesClosed,\n} = require('./state');\nconst { upsertCoordinationWorkItem } = require('./store');\nconst { extractIssueReferences, extractTasks } = require('./parsing');\n\nfunction assertValidRepo(repo) {\n  if (typeof repo !== 'string' || !repo.trim()) {\n    throw new Error(`invalid repo: expected non-empty string, got ${JSON.stringify(repo)}`);\n  }\n}\n\nfunction assertValidIssueNumber(issueNumber) {\n  if (!Number.isFinite(issueNumber) || issueNumber <= 0 || !Number.isInteger(issueNumber)) {\n    throw new Error(`invalid issueNumber: expected positive integer, got ${JSON.stringify(issueNumber)}`);\n  }\n}\n\nfunction staleCoordinationLabels(issue, nextLabels, policy) {\n  const epicLabel = policy.labels && policy.labels.epic;\n  return normalizeLabels(issue.labels).filter(l =>\n    (l.startsWith('coordination:') || l === epicLabel) && !nextLabels.includes(l)\n  );\n}\n\n// applyClaim performs a read (getIssue) → check (assertIssueClaimable) → write\n// (editIssue) sequence that is NOT atomic. Two concurrent callers can both read\n// an unclaimed issue, pass the check, and both succeed — resulting in a\n// double-claim. A code-review finding suggested fixing this via\n// context.store.acquireLock(repo, issueNumber), but that API does not exist in\n// store.js; adding a call to it would throw at runtime. Left as-is until a\n// locking primitive is available — callers should prevent races via external\n// serialization (e.g. a serialized job queue or GitHub branch-protection rule).","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/github-coordination/actions.js#L9-L45","documentation":"assertValidIssueNumber validates the issue identifier before any GitHub operation. It must be a finite positive integer; NaN, Infinity, zero, negatives, floats, and non-numeric values are rejected. Every action that mutates or inspects a specific issue (claim, validate, publish, review, decompose) runs this guard first.","triggerScenarios":"Calling applyClaim/applyValidate/applyPublish/applyReview/applyDecompose with issueNumber from parseInt of a non-numeric string (NaN), a string like '42', a float like 42.5, or 0/-1 from a failed parse.","commonSituations":"Parsing an issue URL with a regex that captures extra text; CLI argument '--issue abc'; reading an issue number from a branch name where the prefix isn't stripped; JSON config containing the issue number as a string.","solutions":["Coerce and validate the issue number: Number(value) and Number.isInteger check before calling","Extract the number with a strict pattern, e.g. /#?(\\d+)$/ on the issue reference","Ensure CLI/config sources supply the raw numeric value, not a URL or string","Fix parseInt fallbacks so failures produce a clear error instead of NaN downstream"],"exampleFix":"// before\napplyClaim(repo, parseInt(arg, 10), {}) // NaN when arg is '#abc'\n// after\nconst m = /#?(\\d+)$/.exec(String(arg));\nif (!m) throw new Error(`bad issue ref: ${arg}`);\napplyClaim(repo, Number(m[1]), {})","handlingStrategy":"type-guard","validationCode":"function assertIssueNumber(n) { if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) throw new Error(`bad issue number: ${JSON.stringify(n)}`); }\nassertIssueNumber(issueNumber);","typeGuard":"const isIssueNumber = (v) => typeof v === 'number' && Number.isInteger(v) && v > 0 && Number.isFinite(v);","tryCatchPattern":"try { applyPublish(repo, n, opts); } catch (e) { if (String(e.message).startsWith('invalid issueNumber')) console.error('issue number came from a bad parse; extract digits strictly'); throw e; }","preventionTips":["Extract issue numbers with /#?(\\d+)$/ instead of bare parseInt","Convert string config values with Number() and validate before use","Never trust issue numbers parsed from branch names or URLs without a strict pattern","Reject NaN/0 early at the CLI argument boundary"],"tags":["validation","github","argument"],"backgroundTag":"invalid-argument-value","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}