sipeed/picoclaw · error
Failed to load config
Error message
Failed to load config
What it means
Thrown in the react-query queryFn of ConfigPage (web/frontend/src/components/config/config-page.tsx:126) when GET /api/config through launcherFetch resolves with a non-2xx status. launcherFetch sends same-origin credentials and redirects to /launcher-login on 401 JSON responses, so this message surfaces for other statuses (500, 404, 403) or a 401 received while already on an auth page. A separate 5-second AbortController timer races the fetch; an abort rejects with AbortError before this throw, so this error strictly means 'server answered with an error status'.
Source
Thrown at web/frontend/src/components/config/config-page.tsx:126
useState<LauncherForm>(EMPTY_LAUNCHER_FORM)
const [launcherBaseline, setLauncherBaseline] =
useState<LauncherForm>(EMPTY_LAUNCHER_FORM)
const [autoStartEnabled, setAutoStartEnabled] = useState(false)
const [autoStartBaseline, setAutoStartBaseline] = useState(false)
const [saving, setSaving] = useState(false)
const [showFactoryResetDialog, setShowFactoryResetDialog] = useState(false)
const { data, isLoading, error } = useQuery({
queryKey: ["config"],
queryFn: async () => {
const controller = new AbortController()
const timer = setTimeout(() => controller.abort(), 5000)
try {
const res = await launcherFetch("/api/config", {
signal: controller.signal,
})
if (!res.ok) {
throw new Error("Failed to load config")
}
return res.json()
} finally {
clearTimeout(timer)
}
},
})
const { data: launcherConfig, isLoading: isLauncherLoading } = useQuery({
queryKey: ["system", "launcher-config"],
queryFn: getLauncherConfig,
})
const { data: versionInfo } = useQuery({
queryKey: ["system", "version"],
queryFn: getSystemVersionInfo,
staleTime: 5 * 60 * 1000,
})View on GitHub (pinned to 49183d7e8d)
Solutions
- Open the network tab and read the actual status and body of GET /api/config — the fix depends on the status code
- For 500: check launcher backend logs and validate the config file it loads (permissions, syntax)
- For 404/502 behind a proxy: fix proxy routing so /api/config reaches the launcher backend
- For 401: log in at /launcher-login, or retry after the backend finishes restarting
Example fix
// before
if (!res.ok) {
throw new Error("Failed to load config")
}
// after
if (!res.ok) {
const body = await res.text().catch(() => "")
throw new Error(`Failed to load config (HTTP ${res.status})${body ? ": " + body : ""}`)
} Defensive patterns
Strategy: retry
Validate before calling
async function pingConfig(): Promise<boolean> {
try {
const res = await launcherFetch("/api/config", { method: "HEAD" })
return res.ok
} catch {
return false
}
} Try / catch
// react-query: let the query error state carry it; distinguish timeout from HTTP error
try {
const res = await launcherFetch("/api/config", { signal: controller.signal })
if (!res.ok) throw new Error(`Failed to load config (HTTP ${res.status})`)
} catch (err) {
if (err instanceof DOMException && err.name === "AbortError") {
// 5s timeout — treat as transient, allow retry
}
throw err
} Prevention
- Keep the backend health-checked and surface its status before the config page loads
- Include the HTTP status in the thrown message so users can self-diagnose 500 vs 401 vs 404
- Retry transient 502/503 once; never retry 4xx blindly
- Ensure reverse-proxy /api routes are tested in deployment smoke checks
When it happens
Trigger: Launcher backend returned 500 for /api/config (config file unreadable/corrupt), 404 when the frontend is served from a different base path, 403 from an auth-protected setup, or 401 while the page is the login/setup page itself (redirect suppressed by isLauncherAuthPath).
Common situations: Config file on disk has bad permissions or invalid TOML/JSON so the backend fails to serialize it; frontend deployed behind a reverse proxy that routes /api elsewhere; session cookie expired exactly while on the login page; backend restarting and returning 502/503 from the proxy.
Related errors
- Failed to fetch config
- Failed to save config
- Request failed with status ${res.status}
- fetch models: %w
- requesting device code: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/38a4a1e5d8e713cd.
Report an issue: GitHub.