AykutSarac/jsoncrack.com · error

Failed to fetch JSON!

Error message

Failed to fetch JSON!

What it means

Toast shown when fetching a remote URL via the Import modal fails. The promise chain is fetch(url).then(res => res.json()) with a single .catch. It fires for network errors, non-2xx responses, CORS rejections, or a response body that is not valid JSON (res.json() throws SyntaxError). All failure modes collapse into the same generic message.

Source

Thrown at apps/www/src/features/modals/ImportModal/index.tsx:31

  const [file, setFile] = React.useState<File | null>(null);

  const setContents = useFile(state => state.setContents);
  const setFormat = useFile(state => state.setFormat);

  const handleImportFile = () => {
    if (url) {
      setFile(null);

      toast.loading("Loading...", { id: "toastFetch" });
      gaEvent("fetch_url");

      return fetch(url)
        .then(res => res.json())
        .then(json => {
          setContents({ contents: JSON.stringify(json, null, 2) });
          onClose();
        })
        .catch(() => toast.error("Failed to fetch JSON!"))
        .finally(() => toast.dismiss("toastFetch"));
    } else if (file) {
      const lastIndex = file.name.lastIndexOf(".");
      const format = file.name.substring(lastIndex + 1);
      setFormat(format as FileFormat);

      file.text().then(text => {
        setContents({ contents: text });
        setFile(null);
        setURL("");
        onClose();
      });

      gaEvent("import_file", { label: format });
    }
  };

  return (

View on GitHub (pinned to 3c9af69e23)

Solutions

  1. Ensure the remote endpoint sends Access-Control-Allow-Origin (or use a CORS proxy in development).
  2. Check res.ok and res.headers content-type before calling res.json(), and throw a clear error otherwise.
  3. Log the caught error (it is currently swallowed) to diagnose network vs parse failure.
  4. Verify the URL returns strict JSON by opening it directly in the browser first.

Example fix

// before
return fetch(url)
  .then(res => res.json())
  .then(json => { setContents({ contents: JSON.stringify(json, null, 2) }); onClose(); })
  .catch(() => toast.error("Failed to fetch JSON!"))

// after — distinguish HTTP vs parse vs network
return fetch(url)
  .then(res => {
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return res.json();
  })
  .then(json => { setContents({ contents: JSON.stringify(json, null, 2) }); onClose(); })
  .catch(err => toast.error(err instanceof SyntaxError ? "Response is not valid JSON" : `Failed to fetch JSON: ${err.message}`))
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check reachability and content-type before parsing the body
async function safeFetchJson(url: string): Promise<unknown> {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const type = res.headers.get("content-type") ?? "";
  if (!type.includes("json")) throw new Error(`Expected JSON, got ${type}`);
  return res.json();
}

Type guard

// Confirm a value is JSON-parseable
export function looksLikeJson(text: string): boolean {
  try { JSON.parse(text); return true; } catch { return false; }
}

Try / catch

// Separate network, HTTP, and parse failures
try {
  const json = await safeFetchJson(url);
  setContents({ contents: JSON.stringify(json, null, 2) });
} catch (err) {
  toast.error(`Failed to fetch JSON: ${err.message}`);
}

Prevention

When it happens

Trigger: Entering a URL that is unreachable, returns HTML/text instead of JSON, lacks CORS headers (browser blocks the read), returns 4xx/5xx, or requires auth. The catch at ImportModal/index.tsx:31 swallows the error without logging it.

Common situations: Fetching from an API without enabling CORS on the server; pasting a URL to an HTML error page; pointing at a private/local network URL from a deployed site; URL returning JSON wrapped in JSONP or with a BOM.

Related errors


AI-assisted analysis of AykutSarac/jsoncrack.com@3c9af69e23 (2026-08-12). Data as JSON: /api/errors/e39b42025371f076. Report an issue: GitHub.