{"record":{"id":"6f095bb8c3b19372","repo":"666ghj/MiroFish","slug":"github-graphql-returned-an-invalid-edge","errorCode":null,"errorMessage":"GitHub GraphQL returned an invalid edge","messagePattern":"GitHub GraphQL returned an invalid edge","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"warning","filePath":"scripts/star_history.py","lineNumber":274,"sourceCode":"        if not all(\n            isinstance(value, dict)\n            for value in (data, repository, stargazers, rate_limit, page_info)\n        ):\n            raise StarHistoryError(\"GitHub GraphQL response had an unexpected shape\")\n\n        total_count = _strict_non_negative_int(\n            stargazers.get(\"totalCount\"), \"GraphQL totalCount\"\n        )\n        rate_remaining = _strict_non_negative_int(\n            rate_limit.get(\"remaining\"), \"GraphQL rate remaining\"\n        )\n        if not isinstance(raw_edges, list):\n            raise StarHistoryError(\"GitHub GraphQL edges were not a list\")\n\n        edges: list[StargazerEdge] = []\n        for raw_edge in raw_edges:\n            if not isinstance(raw_edge, dict):\n                raise StarHistoryError(\"GitHub GraphQL returned an invalid edge\")\n            cursor = raw_edge.get(\"cursor\")\n            starred_at = raw_edge.get(\"starredAt\")\n            if not isinstance(cursor, str) or not cursor:\n                raise StarHistoryError(\"GitHub GraphQL returned an invalid edge cursor\")\n            if not isinstance(starred_at, str):\n                raise StarHistoryError(\"GitHub GraphQL returned an invalid star timestamp\")\n            edges.append(StargazerEdge(cursor, _parse_github_timestamp(starred_at)))\n\n        has_next_page = page_info.get(\"hasNextPage\")\n        end_cursor = page_info.get(\"endCursor\")\n        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(","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L256-L292","documentation":"While iterating the stargazers edges list, one element was not a JSON object. Each edge must be a dict so .get('cursor') and .get('starredAt') work; anything else (string, number, null, nested list) raises StarHistoryError('GitHub GraphQL returned an invalid edge').","triggerScenarios":"An edges array containing null entries (deleted/hidden user accounts can serialize as null nodes), or an edges array of scalars from a hand-edited query or a test fixture.","commonSituations":"Real GitHub responses where an edge's node is null (user deleted their account between starring and pagination); test fixtures built by hand with edges: ['cursor1', 'cursor2'].","solutions":["Log the offending edge value to confirm whether it is null or a scalar","If null edges from deleted accounts are observed, filter them before parsing (GitHub documents that edges may contain null)","Correct test fixtures so edges entries are objects with cursor/starredAt keys"],"exampleFix":"// before\nfor raw_edge in raw_edges:\n    if not isinstance(raw_edge, dict):\n        raise StarHistoryError(\"GitHub GraphQL returned an invalid edge\")\n\n// after\nfor raw_edge in raw_edges:\n    if raw_edge is None:\n        continue  # skip edges for deleted accounts\n    if not isinstance(raw_edge, dict):\n        raise StarHistoryError(\"GitHub GraphQL returned an invalid edge\")","handlingStrategy":"validation","validationCode":"clean_edges = [e for e in raw_edges if isinstance(e, dict)]\nif len(clean_edges) != len(raw_edges):\n    logger.warning(\"dropped %d non-object edges\", len(raw_edges) - len(clean_edges))","typeGuard":"def is_edge_object(edge: object) -> TypeGuard[dict]:\n    return isinstance(edge, dict)","tryCatchPattern":"try:\n    page = _parse_graphql_page(payload)\nexcept StarHistoryError as exc:\n    if \"invalid edge\" in str(exc):\n        logger.warning(\"skipping malformed edge batch: %s\", exc)\n        # safe only if you can re-fetch the page; do not silently lose stars\n        raise\n    raise","preventionTips":["Filter null edges (deleted accounts) before parsing — GitHub documents edges may contain null","Build fixtures from recorded real responses rather than hand-typing edge structures"],"tags":["github-api","graphql","parsing","validation","star-history"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}