AykutSarac/jsoncrack.com · error

Failed to fetch document from URL!

Error message

Failed to fetch document from URL!

What it means

Toast from useFile.fetchUrl when the fetch or response parse fails. The action does fetch(url) then res.json(); the catch clears ALL editor content (get().clear()) and shows this toast. Note it does not check res.ok, so a 4xx/5xx response that returns HTML will also throw at res.json() and trigger the destructive clear.

Source

Thrown at apps/www/src/store/useFile.ts:139

    } catch (error: any) {
      if (error?.mark?.snippet) return set({ error: error.mark.snippet });
      if (error?.message) set({ error: error.message });
      useJson.setState({ loading: false });
    }
  },
  setError: error => set({ error }),
  setHasChanges: hasChanges => set({ hasChanges }),
  fetchUrl: async url => {
    try {
      const res = await fetch(url);
      const json = await res.json();
      const jsonStr = JSON.stringify(json, null, 2);

      get().setContents({ contents: jsonStr });
      return useJson.setState({ json: jsonStr, loading: false });
    } catch {
      get().clear();
      toast.error("Failed to fetch document from URL!");
    }
  },
  checkEditorSession: (url, widget) => {
    if (url && typeof url === "string" && isURL(url)) {
      return get().fetchUrl(url);
    }

    let contents = defaultJson;
    const sessionContent = sessionStorage.getItem("content") as string | null;
    const format = sessionStorage.getItem("format") as FileFormat | null;
    if (sessionContent && !widget) contents = sessionContent;

    if (format) set({ format });
    get().setContents({ contents, hasChanges: false });
  },
}));

export default useFile;

View on GitHub (pinned to 3c9af69e23)

Solutions

  1. Check res.ok and the content-type before calling res.json() to avoid treating HTML errors as JSON failures.
  2. Do not call get().clear() on fetch failure — keep existing content so users do not lose their work.
  3. Log the caught error to distinguish network from parse failure.
  4. Ensure the endpoint sends CORS headers and returns strict JSON.

Example fix

// before
fetchUrl: async url => {
  try {
    const res = await fetch(url);
    const json = await res.json();
    const jsonStr = JSON.stringify(json, null, 2);
    get().setContents({ contents: jsonStr });
    return useJson.setState({ json: jsonStr, loading: false });
  } catch {
    get().clear();
    toast.error("Failed to fetch document from URL!");
  }
},

// after — preserve content, validate response
fetchUrl: async url => {
  try {
    const res = await fetch(url);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const json = await res.json();
    const jsonStr = JSON.stringify(json, null, 2);
    get().setContents({ contents: jsonStr });
    return useJson.setState({ json: jsonStr, loading: false });
  } catch (err) {
    toast.error(`Failed to fetch document from URL: ${err.message}`);
  }
},
Defensive patterns

Strategy: validation

Validate before calling

// Validate URL + response before ingestion; avoid destructive clear
export async function fetchJsonSafe(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(`Not JSON (${type})`);
  return res.json();
}

Type guard

// Confirm a URL string is well-formed http(s)
export function isHttpUrl(value: string): boolean {
  try { const u = new URL(value); return u.protocol === "http:" || u.protocol === "https:"; }
  catch { return false; }
}

Try / catch

// Preserve content; report the real HTTP/parse cause
try {
  const json = await fetchJsonSafe(url);
  get().setContents({ contents: JSON.stringify(json, null, 2) });
} catch (err) {
  toast.error(`Failed to fetch document from URL: ${err.message}`);
}

Prevention

When it happens

Trigger: URL unreachable; CORS rejection; response is not JSON (e.g. an HTML error page); 4xx/5xx; network offline. Because get().clear() runs in the catch, the editor content is wiped on any failure — a notable side effect.

Common situations: Loading a URL that requires auth; pointing at an endpoint returning JSON-wrapped-in-HTML errors; cross-origin without CORS; offline/spotty network during fetch.

Related errors


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