{"record":{"id":"79f5c9cc86845782","repo":"sipeed/picoclaw","slug":"failed-to-attach-to-gateway-v","errorCode":null,"errorMessage":"Failed to attach to gateway: %v","messagePattern":"Failed to attach to gateway: (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"web/backend/api/gateway.go","lineNumber":1206,"sourceCode":"\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 {\n\t\t\tgateway.mu.Unlock()\n\t\t\tlogger.ErrorC(\"gateway\", fmt.Sprintf(\"Failed to attach to running gateway (PID: %d): %v\", pid, err))\n\t\t\thttp.Error(w, fmt.Sprintf(\"Failed to attach to gateway: %v\", err), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tgateway.pidData = pidData\n\t\tgateway.mu.Unlock()\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\tw.WriteHeader(http.StatusOK)\n\t\tjson.NewEncoder(w).Encode(map[string]any{\n\t\t\t\"status\": \"ok\",\n\t\t\t\"pid\":    pid,\n\t\t})\n\t\treturn\n\t}\n\n\tgateway.mu.Lock()\n\tdefer gateway.mu.Unlock()\n\n\tif gateway.cmd != nil && gateway.cmd.Process != nil {\n\t\tgateway.cmd = nil","sourceCodeStart":1188,"sourceCodeEnd":1224,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/web/backend/api/gateway.go#L1188-L1224","documentation":"Returned by POST /api/gateway/start when a sanitized PID file indicates a running gateway, and h.startGatewayLocked(\"starting\", pid) fails to attach to that PID. The attach path (gateway.go:1008-1028) can fail in config.LoadConfig or in attachToGatewayProcessLocked -> os.FindProcess (gateway.go:854), which fails for an inaccessible/invalid PID on Windows and practically only for races on POSIX. The most common real cause is a race: the gateway process died between the PID-file liveness check and the attach attempt. The backend log line 'Failed to attach to running gateway (PID: N)' carries the wrapped reason.","triggerScenarios":"The gateway crashes or is killed exactly while the start request is in flight, leaving a PID file whose process vanished; on Windows, OpenProcess access-denied for the recorded PID; a stale but momentarily-alive-looking PID file after an unclean shutdown.","commonSituations":"Scripts that pkill the gateway and immediately call the start endpoint; OOM killer reaping the gateway during a start call; PID-file left behind after a host crash while the launcher restarts.","solutions":["Call GET /api/gateway/status to see whether the recorded PID is still alive","Retry POST /api/gateway/start - if the old process truly died, the stale PID file is detected and cleaned, and a fresh gateway starts via the normal path","If it keeps failing, check backend logs for the wrapped error, then remove the stale gateway PID file in the global config dir and start again","On Windows, confirm the backend has permission to open the recorded process handle"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Confirm the PID-file gateway is really alive before asking to attach.\nconst st = await (await fetch('/api/gateway/status')).json();\nif (st.gateway_status !== 'running') {\n  // stale PID file will be cleaned on start; safe to proceed without attach\n}","typeGuard":null,"tryCatchPattern":"for (let attempt = 0; attempt < 2; attempt++) {\n  const res = await fetch('/api/gateway/start', {method: 'POST'});\n  if (res.ok) return res.json();\n  const text = await res.text();\n  if (res.status === 500 && text.startsWith('Failed to attach')) {\n    await sleep(1500);                    // let the dead-PID race settle\n    continue;                             // retry: stale PID file gets cleaned, fresh start succeeds\n  }\n  throw new Error(text);\n}","preventionTips":["Do not pkill the gateway and immediately call start - leave a second for PID-file cleanup","Prefer POST /api/gateway/stop over external kills so tracked state stays consistent","Poll /api/gateway/status after kills to confirm the process is gone before starting"],"tags":["gateway","process","race-condition","pid-file","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}