Skip to content

Guard fetch call sites against non-JSON (HTML) responses#90

Draft
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-code/guard-non-json-fetch-responses
Draft

Guard fetch call sites against non-JSON (HTML) responses#90
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-code/guard-non-json-fetch-responses

Conversation

@posthog

@posthog posthog Bot commented Jul 1, 2026

Copy link
Copy Markdown

Summary

The frontend threw an uncaught SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON whenever an API call returned an HTML page instead of JSON. Across the page components we called await fetch(url) and then immediately await response.json() without checking response.ok or the content-type, so an HTML body (a gateway/error page or an auth redirect) made .json() throw. Layout.tsx's fetchHostname runs on every page load and had no try/catch, making it the one spot that produced a genuinely uncaught error.

This adds a small shared helper and routes the fetch call sites through it so a transient HTML response degrades gracefully instead of throwing.

  • New frontend/src/utils/api.ts exporting fetchJson (and HttpResponseError): checks the content-type before calling .json() and throws a clean, catchable error when the body isn't JSON. JSON responses are still returned as-is regardless of status code, so existing inline error-field handling keeps working.
  • Routed the ~20 fetch().json() call sites through fetchJson.
  • Gave Layout.tsx's fetchHostname the missing try/catch.

Non-JSON-parsing calls (a DELETE that reads res.text(), and the status-only POSTs) are left as raw fetch since they can't hit the SyntaxError.

Why

Hardening a defensive pattern that spans the whole frontend: a stray HTML response (5xx page, redirect, Django error page) should no longer crash the UI. This is a robustness cleanup rather than a fix for an active production incident.

Test plan

  • tsc --noEmit reports zero errors in src/.

Created with PostHog Code from an inbox report.

Add a shared `fetchJson` helper that checks `response.ok` and the content-type
before calling `.json()`, throwing a clean, catchable `HttpResponseError` when the
body isn't JSON (e.g. a 5xx gateway page, an auth redirect, or a Django error page
starting with `<!DOCTYPE html>`) instead of letting `.json()` blow up with an
uncaught `SyntaxError: Unexpected token '<'`.

Route the ~20 frontend fetch call sites through it, and give `Layout.tsx`'s
`fetchHostname` the try/catch it was missing (the one spot that produced an
uncaught error on every page load).

Generated-By: PostHog Code
Task-Id: 91df810a-1048-4296-9272-4630a410b5fc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants