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

  1. Check the network tab for the exact status of GET /api/config and read the response body
  2. For 500: inspect launcher backend logs and repair the config file on disk (permissions, syntax)
  3. For proxy errors (404/502): correct routing so /api/config hits the launcher backend
  4. 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

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


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/16ba7685e005f756. Report an issue: GitHub.