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
- Verify donateUrl resolves in a browser; correct GITHUB_CONFIG owner/repo/path if 404
- If 403, back off the rate limit or reduce no-store refetch frequency / cache the result
- Check connectivity to raw.githubusercontent.com (proxy/firewall)
- 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
- Bundle a static fallback donate payload in the app
- Soften aggressive no-store refetching to avoid GitHub rate limits
- Pin donateUrl to a stable ref/commit
- Alert on 404s after repo renames by checking the URL in CI
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
- `HTTP ${res.status}`
- Failed to fetch image: ${res.status}
- Failed to get device code: ${error}
- Failed to get Copilot token: ${error}
- Failed to get user info: ${error}
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/cbd2a6e28034a356.
Report an issue: GitHub.