{"record":{"id":"905983b867ca6490","repo":"sipeed/picoclaw","slug":"failed-to-validate-gateway-start-conditions-v","errorCode":null,"errorMessage":"Failed to validate gateway start conditions: %v","messagePattern":"Failed to validate gateway start conditions: (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"web/backend/api/gateway.go","lineNumber":1185,"sourceCode":"\t\t}\n\t}()\n\n\treturn pid, nil\n}\n\n// handleGatewayStart starts the picoclaw gateway subprocess.\n//\n//\tPOST /api/gateway/start\nfunc (h *Handler) handleGatewayStart(w http.ResponseWriter, r *http.Request) {\n\t// Check PID file first to detect an already-running gateway.\n\tpidData := h.sanitizeGatewayPidData(ppid.ReadPidFileWithCheck(globalConfigDir()), nil)\n\tif pidData != nil {\n\t\tpid := pidData.PID\n\t\tgateway.mu.Lock()\n\t\tready, reason, err := h.gatewayStartReady()\n\t\tif err != nil {\n\t\t\tgateway.mu.Unlock()\n\t\t\thttp.Error(\n\t\t\t\tw,\n\t\t\t\tfmt.Sprintf(\"Failed to validate gateway start conditions: %v\", err),\n\t\t\t\thttp.StatusInternalServerError,\n\t\t\t)\n\t\t\treturn\n\t\t}\n\t\tif !ready {\n\t\t\tgateway.mu.Unlock()\n\t\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\t\tw.WriteHeader(http.StatusBadRequest)\n\t\t\tjson.NewEncoder(w).Encode(map[string]any{\n\t\t\t\t\"status\":  \"precondition_failed\",\n\t\t\t\t\"message\": reason,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\t_, err = h.startGatewayLocked(\"starting\", pid)\n\t\tif err != nil {","sourceCodeStart":1167,"sourceCodeEnd":1203,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/web/backend/api/gateway.go#L1167-L1203","documentation":"Returned by POST /api/gateway/start when a live PID file exists and h.gatewayStartReady() returns an error. Reading gateway.go:371-375, the only error path in gatewayStartReady is config.LoadConfig(h.configPath) failing - the picoclaw config file cannot be read or parsed. This is deliberately distinct from the 400 precondition_failed branch, which handles semantic problems (no default model, invalid model, missing credentials, unreachable local model). A 500 here means the config file itself is unreadable or syntactically broken.","triggerScenarios":"config.json/yaml was hand-edited and left with a syntax error while the launcher runs; the config file was deleted, moved, or truncated; file permissions changed so the backend user can no longer read it; the config sits on a network mount that just dropped.","commonSituations":"Editing the config in an external editor with an unsaved/invalid intermediate state; running the web backend under a different user (systemd unit, container) than the one owning the config file; a crashed disk-full write left a zero-length config.","solutions":["Call GET /api/config - it hits the same config.LoadConfig and its error text shows the exact parse/read failure","Fix the reported syntax error, or restore the config file from backup","Verify the config file is readable by the user running the backend process (check ownership and mode)","Retry POST /api/gateway/start once the config loads cleanly"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Probe config loadability before starting the gateway -\n// GET /api/config exercises the same config.LoadConfig call.\nconst probe = await fetch('/api/config');\nif (!probe.ok) {\n  const detail = await probe.text();   // e.g. the YAML/JSON parse error\n  throw new Error('config file is broken, fix before gateway start: ' + detail);\n}","typeGuard":null,"tryCatchPattern":"const res = await fetch('/api/gateway/start', {method: 'POST'});\nif (res.status === 500) {\n  const text = await res.text();\n  if (text.startsWith('Failed to validate gateway start conditions')) {\n    // infrastructure failure: config unreadable -> route user to config repair, do NOT retry blindly\n    await router.push('/settings/config');\n  }\n  throw new Error(text);\n}","preventionTips":["Never hand-edit the config file while the launcher is running; use PUT /api/config so writes are validated","Watch GET /api/config for 500s as a canary in health checks","Keep config file ownership aligned with the user running the backend"],"tags":["gateway","config","go","process-management","picoclaw-api"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}