decolua/9router · warning · Error

`HTTP ${res.status}`

Error message

`HTTP ${res.status}`

What it means

DonateModal fetches JSON (sponsor/donate data) from GITHUB_CONFIG.donateUrl with cache:'no-store' and throws `HTTP ${res.status}` for any non-ok response. The effect catches it and shows the status string as the modal error. Like ChangelogModal this is a raw status passthrough with no response body detail.

Source

Thrown at src/shared/components/DonateModal.js:20

import { useEffect, useState, useRef } from "react";
import { createPortal } from "react-dom";
import PropTypes from "prop-types";
import { GITHUB_CONFIG } from "@/shared/constants/config";

export default function DonateModal({ isOpen, onClose }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const modalRef = useRef(null);

  useEffect(() => {
    if (!isOpen || data) return;
    setLoading(true);
    setError("");
    fetch(GITHUB_CONFIG.donateUrl, { cache: "no-store" })
      .then((res) => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return res.json();
      })
      .then((json) => setData(json))
      .catch((err) => setError(err.message || "Failed to load"))
      .finally(() => setLoading(false));
  }, [isOpen, data]);

  useEffect(() => {
    const handleClickOutside = (e) => {
      if (modalRef.current && !modalRef.current.contains(e.target)) onClose();
    };
    if (isOpen) {
      document.addEventListener("mousedown", handleClickOutside);
      return () => document.removeEventListener("mousedown", handleClickOutside);
    }
  }, [isOpen, onClose]);

  if (!isOpen || typeof document === "undefined") return null;

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Verify donateUrl resolves in a browser; correct GITHUB_CONFIG owner/repo/path if 404
  2. If 403, back off the rate limit or reduce no-store refetch frequency / cache the result
  3. Check connectivity to raw.githubusercontent.com (proxy/firewall)
  4. Ship a graceful fallback UI (static donate addresses) when fetch fails

Example fix

// before
if (!res.ok) throw new Error(`HTTP ${res.status}`);
// after
if (!res.ok) throw new Error(`Donation info unavailable (HTTP ${res.status})`);
Defensive patterns

Strategy: fallback

Validate before calling

const res = await fetch(GITHUB_CONFIG.donateUrl, { method: "HEAD", cache: "no-store" });
if (!res.ok) useStaticDonateFallback();

Type guard

function isFetchOk(res) { return Boolean(res && res.ok); }

Try / catch

try {
  const res = await fetch(GITHUB_CONFIG.donateUrl, { cache: "no-store" });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  setData(await res.json());
} catch (err) {
  setError(err.message || "Failed to load");
  setData(FALLBACK_DONATE_DATA); // static addresses
}

Prevention

When it happens

Trigger: fetch(GITHUB_CONFIG.donateUrl) returning 404 (file/repo moved), 403 (GitHub rate limit or private repo), or 5xx; also a non-JSON endpoint would fail later at res.json(), but the thrown error here is specifically non-ok status.

Common situations: Misconfigured GITHUB_CONFIG owner/repo after renaming; rate-limited by GitHub due to no-store refetching every modal open; offline/proxied environments; donate JSON file deleted from the repo.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/cbd2a6e28034a356. Report an issue: GitHub.