{"record":{"id":"653703a81961a6aa","repo":"QuantumNous/new-api","slug":"unexpected-release-payload","errorCode":null,"errorMessage":"Unexpected release payload","messagePattern":"Unexpected release payload","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"web/src/features/system-settings/maintenance/update-checker-section.tsx","lineNumber":75,"sourceCode":"    setChecking(true)\n    try {\n      const response = await fetch(\n        'https://api.github.com/repos/Calcium-Ion/new-api/releases/latest',\n        {\n          headers: {\n            Accept: 'application/vnd.github+json',\n            'User-Agent': 'new-api-dashboard',\n          },\n        }\n      )\n\n      if (!response.ok) {\n        throw new Error(t('Failed to contact GitHub releases API'))\n      }\n\n      const data = (await response.json()) as ReleaseInfo\n      if (!data?.tag_name) {\n        throw new Error(t('Unexpected release payload'))\n      }\n\n      if (currentVersion && data.tag_name === currentVersion) {\n        toast.success(\n          t('You are running the latest version ({{version}}).', {\n            version: data.tag_name,\n          })\n        )\n        return\n      }\n\n      setRelease(data)\n      setDialogOpen(true)\n    } catch (error) {\n      const message =\n        error instanceof Error\n          ? error.message\n          : t('Failed to check for updates')","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/system-settings/maintenance/update-checker-section.tsx#L57-L93","documentation":"Thrown when the GitHub releases response is HTTP 200 but the JSON lacks tag_name. The dashboard casts the parsed body to ReleaseInfo without runtime validation, so a proxy interstitial, captive portal HTML page parsed as JSON, or API schema change trips this guard.","triggerScenarios":"response.json() succeeds but yields an object without tag_name: a transparent proxy returning an HTML/JSON hybrid, GitHub API schema drift, an empty object from a caching layer, or a mirror endpoint with a different response shape.","commonSituations":"Corporate/ISP captive portals intercepting api.github.com; CDN cache serving a truncated body; future GitHub API changes renaming tag_name; custom build environments pointing at a GitHub Enterprise endpoint with a different payload.","solutions":["Log/inspect the actual parsed body when this fires to see what the endpoint really returned.","If behind an intercepting proxy, fix network egress so the genuine GitHub API responds, or route the check through the backend.","Handle body.html_url, body.name, and published_at with defaults so only tag_name is truly mandatory (already the case).","Verify the repo path (Calcium-Ion/new-api) still exists and the releases API is not returning a 404-shaped 200 from a mirror."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"interface ReleaseInfo { tag_name: string; name?: string; html_url?: string; published_at?: string }\nfunction isReleaseInfo(data: unknown): data is ReleaseInfo {\n  return typeof data === 'object' && data !== null &&\n    typeof (data as ReleaseInfo).tag_name === 'string' &&\n    (data as ReleaseInfo).tag_name.length > 0\n}","tryCatchPattern":"const data: unknown = await response.json()\nif (!isReleaseInfo(data)) {\n  throw new Error(t('Unexpected release payload'))\n}","preventionTips":["Parse as unknown and narrow with a type guard instead of casting response.json() directly.","Detect non-JSON/proxy interstitials via content-type check before parsing.","Keep the update checker non-fatal: log the raw payload shape when validation fails to speed diagnosis."],"tags":["update-checker","github-api","validation","network"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}