sipeed/picoclaw · error
Failed to fetch config
Error message
Failed to fetch config
What it means
Thrown in the react-query queryFn of RawConfigPage (web/frontend/src/components/config/raw-config-page.tsx:36) when GET /api/config via launcherFetch resolves with a non-2xx status. Same endpoint and same launcherFetch wrapper as ConfigPage's loader, but without the 5-second timeout this page uses. 401 JSON responses normally redirect to /launcher-login before this throw, except on auth pages.
Source
Thrown at web/frontend/src/components/config/raw-config-page.tsx:36
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import { Textarea } from "@/components/ui/textarea"
import { showSaveSuccessOrRestartToast } from "@/lib/restart-required"
import { refreshGatewayState } from "@/store/gateway"
export function RawConfigPage() {
const { t } = useTranslation()
const queryClient = useQueryClient()
const { data: config, isLoading } = useQuery({
queryKey: ["config"],
queryFn: async () => {
const res = await launcherFetch("/api/config")
if (!res.ok) {
throw new Error("Failed to fetch config")
}
return res.json()
},
})
const mutation = useMutation({
mutationFn: async (newConfig: string) => {
const res = await launcherFetch("/api/config", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: newConfig,
})
if (!res.ok) {
throw new Error("Failed to save config")
}
},
onSuccess: (_, submittedConfig) => {
try {View on GitHub (pinned to 49183d7e8d)
Solutions
- Check the network tab for the exact status of GET /api/config and read the response body
- For 500: inspect launcher backend logs and repair the config file on disk (permissions, syntax)
- For proxy errors (404/502): correct routing so /api/config hits the launcher backend
- For 401: authenticate at /launcher-login first
Example fix
// before
const res = await launcherFetch("/api/config")
if (!res.ok) {
throw new Error("Failed to fetch config")
}
// after
const res = await launcherFetch("/api/config")
if (!res.ok) {
const body = await res.text().catch(() => "")
throw new Error(`Failed to fetch config (HTTP ${res.status})${body ? ": " + body : ""}`)
} Defensive patterns
Strategy: retry
Validate before calling
async function configReachable(): Promise<boolean> {
try {
const res = await launcherFetch("/api/config")
return res.ok
} catch {
return false
}
} Try / catch
// let react-query expose query.error; enrich with status
const res = await launcherFetch("/api/config")
if (!res.ok) {
const body = await res.text().catch(() => "")
throw new Error(`Failed to fetch config (HTTP ${res.status})${body ? ": " + body : ""}`)
} Prevention
- Run deployment smoke tests that GET /api/config through the same proxy path users hit
- Keep the config file readable by the backend user; alert on permission drift
- Include status codes in thrown errors to cut diagnosis time
When it happens
Trigger: Backend returns 500 (config unreadable/corrupt on disk), 404 from a misrouted reverse proxy, 403, or 401 while on the login/setup page; opening /raw config editor right after a backend restart.
Common situations: Config file permissions broken after an edit; proxy rules covering /api but pointing to the wrong upstream; expired session cookie; backend crashed and the proxy answers 502.
Related errors
- Failed to load 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/16ba7685e005f756.
Report an issue: GitHub.