{"record":{"id":"20aa5fb26365a414","repo":"sipeed/picoclaw","slug":"failed-to-restart-gateway-v","errorCode":null,"errorMessage":"Failed to restart gateway: %v","messagePattern":"Failed to restart gateway: (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"web/backend/api/gateway.go","lineNumber":1385,"sourceCode":"\n// handleGatewayRestart stops the gateway (if running) and starts a new instance.\n//\n//\tPOST /api/gateway/restart\nfunc (h *Handler) handleGatewayRestart(w http.ResponseWriter, r *http.Request) {\n\tpid, err := h.RestartGateway()\n\tif err != nil {\n\t\t// Check if it's a precondition failed error\n\t\tvar precondErr *preconditionFailedError\n\t\tif errors.As(err, &precondErr) {\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\": precondErr.reason,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\thttp.Error(w, fmt.Sprintf(\"Failed to restart gateway: %v\", err), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(map[string]any{\n\t\t\"status\": \"ok\",\n\t\t\"pid\":    pid,\n\t})\n}\n\n// handleGatewayClearLogs clears the in-memory gateway log buffer.\n//\n//\tPOST /api/gateway/logs/clear\nfunc (h *Handler) handleGatewayClearLogs(w http.ResponseWriter, r *http.Request) {\n\tgateway.logs.Clear()\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(map[string]any{","sourceCodeStart":1367,"sourceCodeEnd":1403,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/web/backend/api/gateway.go#L1367-L1403","documentation":"Returned by POST /api/gateway/restart when h.RestartGateway() fails outside precondition validation. Preconditions that merely fail (no default model, bad credentials) are mapped to 400 precondition_failed with a reason; this 500 wraps infrastructure failures from gateway.go:1293-1352: 'failed to validate gateway start conditions' (config unreadable), 'refuse to restart non-gateway process (PID %d)' (PID-reuse guard), 'failed to stop gateway: ...' (old process ignored SIGTERM through the grace period and survived SIGKILL window, or 'existing gateway did not exit before restart'), or 'failed to start gateway: ...' (binary/config/pipe problems as in error 905).","triggerScenarios":"Hung gateway that does not exit within gatewayRestartGracePeriod and even survives the SIGKILL window (uninterruptible D-state); PID reuse triggering the refuse guard; picoclaw binary missing so the start half fails after a successful stop; corrupt config file failing the initial validation.","commonSituations":"Gateway wedged on a dead network mount after config changes; automation restarting immediately after a config swap; containers where the gateway ignores signals because PID 1 does not forward them.","solutions":["Read the wrapped message to branch: stop failures, refuse-guard, config, or binary issues each have different fixes","For 'did not exit before restart', retry POST /api/gateway/restart once - the second attempt starts from a cleared state","For 'refuse to restart non-gateway process', inspect the PID with ps and restart the web backend to reset tracked state","For config/binary causes, apply the fixes from errors 902/905, then retry"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-flight: config loads (GET /api/config ok) is the cheap validation;\n// readiness reasons arrive as 400 precondition_failed with a message.\nconst cfgOk = (await fetch('/api/config')).ok;\nif (!cfgOk) throw new Error('fix config file before restarting gateway');","typeGuard":null,"tryCatchPattern":"for (let attempt = 0; attempt < 3; attempt++) {\n  const res = await fetch('/api/gateway/restart', {method: 'POST'});\n  if (res.ok) return res.json();\n  if (res.status === 400) {\n    const body = await res.json();            // {status:'precondition_failed', message}\n    throw new Error('Fix preconditions first: ' + body.message);   // not retryable\n  }\n  const text = await res.text();\n  if (text.includes('refuse to restart non-gateway process')) throw new Error(text); // not retryable\n  await sleep(2000 * (attempt + 1));          // stop/start infra failure: backoff and retry\n}","preventionTips":["Retry restarts with backoff - a hung old process often exits on the second attempt's escalation path","Distinguish 400 precondition_failed (fix config per message) from 500 (infra) before retrying","Watch /api/gateway/status for a stuck 'restarting' state instead of stacking more restart calls"],"tags":["gateway","process","restart","signals","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}