sipeed/picoclaw · error
Failed to load launcher config: %v
Error message
Failed to load launcher config: %v
What it means
Returned by GET /api/system/launcher-config when launcherconfig.Load of launcher-config.json (path derived via PathForAppConfig next to the app config) fails. From launcherconfig/config.go:102-124: a missing file is fine (server-flag fallback is returned); errors come from reading the file (permission, I/O), invalid JSON, or on-disk values failing Validate - port outside 1-65535, or a CIDR entry that net.ParseCIDR rejects.
Source
Thrown at web/backend/api/launcher_config.go:57
port = launcherconfig.DefaultPort
}
return launcherconfig.Config{
Port: port,
Public: h.serverPublic,
AllowedCIDRs: append([]string(nil), h.serverCIDRs...),
AllowLocalhostBypass: h.serverAllowLocalhostBypass,
TrustedProxyCIDRs: append([]string(nil), h.serverTrustedProxyCIDRs...),
}
}
func (h *Handler) loadLauncherConfig() (launcherconfig.Config, error) {
return launcherconfig.Load(h.launcherConfigPath(), h.launcherFallbackConfig())
}
func (h *Handler) handleGetLauncherConfig(w http.ResponseWriter, r *http.Request) {
cfg, err := h.loadLauncherConfig()
if err != nil {
http.Error(w, fmt.Sprintf("Failed to load launcher config: %v", err), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(launcherConfigPayload{
Port: cfg.Port,
Public: cfg.Public,
AllowedCIDRs: append([]string(nil), cfg.AllowedCIDRs...),
AllowLocalhostBypass: cfg.AllowLocalhostBypass,
TrustedProxyCIDRs: append([]string(nil), cfg.TrustedProxyCIDRs...),
})
}
func (h *Handler) handleUpdateLauncherConfig(w http.ResponseWriter, r *http.Request) {
var payload launcherConfigUpdatePayload
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, fmt.Sprintf("Invalid JSON: %v", err), http.StatusBadRequest)
returnView on GitHub (pinned to 49183d7e8d)
Solutions
- Read the wrapped error: a JSON syntax error points at editing damage; a Validate error names the offending port or CIDR
- Fix the named field, or simply delete launcher-config.json - that is safe, the handler falls back to the server's startup flags
- Ensure the backend process can read the file (ownership and mode)
- Afterward use PUT /api/system/launcher-config for changes so values are always validated before persistence
Defensive patterns
Strategy: try-catch
Try / catch
let res = await fetch('/api/system/launcher-config');
if (res.status === 500) {
const detail = await res.text();
// File-side corruption cannot be fixed through the API - offer a reset action:
if (confirm('launcher-config.json is unreadable (' + detail + '). Delete it on the server to restore defaults?')) {
await serverSideResetLauncherConfig(); // rm the file; GET then falls back to startup flags
res = await fetch('/api/system/launcher-config');
}
} Prevention
- Only modify launcher settings through PUT /api/system/launcher-config - it validates before persisting
- Keep launcher-config.json owned by the service user with mode 0600
- Include GET launcher-config in health checks to catch corruption early
When it happens
Trigger: launcher-config.json hand-edited with a JSON typo; port edited to 0 or 70000; a CIDR written as a bare IP like "192.168.1.5" instead of "192.168.1.5/32"; file owned by root:0600 while the backend runs unprivileged.
Common situations: Sysadmins hand-editing the file instead of using the PUT endpoint; restoring a backup with wrong ownership; schema drift after version upgrades.
Related errors
- Invalid JSON: %v
- ${label} must be a JSON object.
- ${label}.${key} must be a string.
- failed to load config: %w
- invalid WeCom QR generate URL: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/42a87546addd1a11.
Report an issue: GitHub.