{"record":{"id":"b64456763fee9480","repo":"zed-industries/zed","slug":"issue-number-has-no-created-at","errorCode":null,"errorMessage":"issue {number} has no created_at","messagePattern":"issue (.+?) has no created_at","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"script/triage_project_sync.py","lineNumber":263,"sourceCode":"    issue_type: str | None  # e.g. \"Bug\", \"Crash\", \"Meta\", \"Tracking\", or None\n    is_pull_request: bool\n    comments: list[dict]\n\n\ndef parse_dt(s: str | None) -> datetime | None:\n    if not s:\n        return None\n    return datetime.fromisoformat(s.replace(\"Z\", \"+00:00\"))\n\n\ndef fetch_issue(number: int) -> IssueData:\n    issue = rest_get(f\"repos/{REPO}/issues/{number}\")\n    if not isinstance(issue, dict):\n        raise RuntimeError(f\"unexpected response for issue {number}\")\n    comments = rest_get_paginated(f\"repos/{REPO}/issues/{number}/comments\")\n    created_at = parse_dt(issue[\"created_at\"])\n    if created_at is None:\n        raise RuntimeError(f\"issue {number} has no created_at\")\n    issue_type = None\n    if isinstance(issue.get(\"type\"), dict):\n        issue_type = issue[\"type\"].get(\"name\")\n    return IssueData(\n        number=number,\n        node_id=issue[\"node_id\"],\n        title=issue[\"title\"],\n        state=issue[\"state\"],\n        closed_at=parse_dt(issue.get(\"closed_at\")),\n        created_at=created_at,\n        reporter=issue[\"user\"][\"login\"],\n        assignees=[a[\"login\"] for a in (issue.get(\"assignees\") or [])],\n        labels=[l[\"name\"] for l in issue[\"labels\"]],\n        issue_type=issue_type,\n        is_pull_request=\"pull_request\" in issue,\n        comments=comments,\n    )\n","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/script/triage_project_sync.py#L245-L281","documentation":"Raised when parse_dt(issue['created_at']) returns None inside fetch_issue(). parse_dt returns None only for falsy input, so GitHub returned HTTP 200 with an issue object whose created_at is null or an empty string. A genuinely missing key would raise KeyError instead — this error specifically means the field was present but empty.","triggerScenarios":"GET /repos/{REPO}/issues/{number} succeeds (200) but the body has \"created_at\": null or \"\" — a degraded or partially-truncated payload from GitHub, a caching proxy, or an Enterprise Server version with different behavior.","commonSituations":"Transient GitHub incident producing incomplete payloads; an HTTP cache serving a truncated body; GitHub Enterprise Server API contract drift; response body cut mid-transfer and parsed leniently.","solutions":["Re-run the sync — transient payload anomalies usually do not repeat for the same issue","Dump the raw JSON for the failing issue number and confirm created_at is actually empty","If persistent, check api.github.com status / GitHub Enterprise version and report the payload shape"],"exampleFix":"# before\ncreated_at = parse_dt(issue[\"created_at\"])\nif created_at is None:\n    raise RuntimeError(f\"issue {number} has no created_at\")\n\n# after (skip anomalous issues with visibility instead of aborting the sync)\ncreated_at = parse_dt(issue.get(\"created_at\"))\nif created_at is None:\n    log(f\"issue {number} has empty created_at; skipping\", \"WARN\")\n    continue","handlingStrategy":"validation","validationCode":"# Validate the raw payload shape before building IssueData\nissue = rest_get(f\"repos/{REPO}/issues/{number}\")\nif not isinstance(issue.get(\"created_at\"), str) or not issue[\"created_at\"].strip():\n    log(f\"issue {number} has empty created_at; skipping\", \"WARN\")\n    continue","typeGuard":"def has_created_at(payload: dict) -> bool:\n    value = payload.get(\"created_at\")\n    return isinstance(value, str) and value.strip() != \"\"","tryCatchPattern":"try:\n    data = fetch_issue(number)\nexcept RuntimeError as error:\n    if \"no created_at\" in str(error):\n        log(f\"skipping anomalous issue {number}\", \"WARN\")\n        continue\n    raise","preventionTips":["Treat single-issue anomalies as skippable with a warning instead of fatal","Log the raw payload for the failing number when this fires to distinguish null vs empty string","Re-run transient failures before investigating — GitHub payload glitches are usually one-off"],"tags":["python","github-api","data-integrity","triage"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}