{"record":{"id":"8b2e9e17f2e1336a","repo":"666ghj/MiroFish","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":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":303,"sourceCode":"        if type(has_next_page) is not bool:\n            raise StarHistoryError(\"GitHub GraphQL returned invalid page information\")\n        if end_cursor is not None and not isinstance(end_cursor, str):\n            raise StarHistoryError(\"GitHub GraphQL returned an invalid page cursor\")\n        if has_next_page and not end_cursor:\n            raise StarHistoryError(\"GitHub GraphQL omitted the next page cursor\")\n\n        return StargazerPage(\n            total_count=total_count,\n            edges=tuple(edges),\n            has_next_page=has_next_page,\n            end_cursor=end_cursor,\n            rate_remaining=rate_remaining,\n        )\n\n\ndef _strict_non_negative_int(value: Any, label: str) -> int:\n    if type(value) is not int or value < 0:\n        raise StarHistoryError(f\"{label} must be a non-negative integer\")\n    return value\n\n\ndef _parse_github_timestamp(value: str) -> datetime:\n    try:\n        parsed = datetime.fromisoformat(value.replace(\"Z\", \"+00:00\"))\n    except ValueError as exc:\n        raise StarHistoryError(\"GitHub returned an invalid star timestamp\") from exc\n    if parsed.tzinfo is None or parsed.utcoffset() != timedelta(0):\n        raise StarHistoryError(\"GitHub star timestamp was not UTC\")\n    return parsed.astimezone(UTC)\n\n\ndef _parse_state_timestamp(value: Any, label: str) -> datetime:\n    if not isinstance(value, str) or not STATE_TIMESTAMP_RE.fullmatch(value):\n        raise StarHistoryError(f\"{label} must use YYYY-MM-DDTHH:MM:SSZ\")\n    try:\n        parsed = datetime.strptime(value, \"%Y-%m-%dT%H:%M:%SZ\").replace(tzinfo=UTC)","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L285-L321","documentation":"The helper _strict_non_negative_int rejected a value that was not exactly an int (type(value) is not int — bools and floats fail) or was negative. It is used for stargazers.totalCount and rateLimit.remaining, raising StarHistoryError(f'{label} must be a non-negative integer') with the caller-supplied label such as 'GraphQL totalCount'.","triggerScenarios":"totalCount: null when the totalCount field was not selected in the query; rateLimit.remaining as a float or string from a proxy; a negative or missing rateLimit block (e.g. query edited to drop rateLimit).","commonSituations":"Editing the GraphQL query and forgetting totalCount or rateLimit { remaining } in the selection; fixtures using floats; GitHub returning rateLimit: null when the query omits it.","solutions":["Ensure the query selects totalCount on stargazers and rateLimit { remaining } at the top level","Check the error's label to see which field failed, then inspect that field in the raw response","Fix fixtures to use plain non-negative ints (not floats or numeric strings)"],"exampleFix":"// before (query fragment)\nquery($after: String) { rateLimit { cost } }\n\n// after (query fragment)\nquery($after: String) { rateLimit { remaining } }","handlingStrategy":"validation","validationCode":"for label, value in ((\"totalCount\", sg.get(\"totalCount\")), (\"rate remaining\", rl.get(\"remaining\"))):\n    if type(value) is not int or value < 0:\n        raise StarHistoryError(f\"{label} must be a non-negative integer\")","typeGuard":"def is_strict_non_negative_int(value: object) -> TypeGuard[int]:\n    return type(value) is int and value >= 0","tryCatchPattern":"try:\n    total = _strict_non_negative_int(sg.get(\"totalCount\"), \"GraphQL totalCount\")\nexcept StarHistoryError as exc:\n    raise StarHistoryError(\"query must select stargazers.totalCount as an int\") from exc","preventionTips":["Always select totalCount and rateLimit { remaining } in the query","Remember bool is an int subclass but type() checks reject it — use real ints in fixtures"],"tags":["github-api","graphql","validation","type-check","star-history"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}