{"record":{"id":"a24eb3bf48fbf3d5","repo":"chenhg5/cc-connect","slug":"create-device-w","errorCode":null,"errorMessage":"create device: %w","messagePattern":"create device: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/webex/webex.go","lineNumber":306,"sourceCode":"\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase <-time.After(backoff):\n\t\t}\n\t}\n}\n\nfunc (p *Platform) lifecycle() core.PlatformLifecycleHandler {\n\tp.mu.RLock()\n\tdefer p.mu.RUnlock()\n\treturn p.lifecycleHandler\n}\n\n// runConnection registers a device, dials the WebSocket, and reads until the\n// connection drops or the context is cancelled.\nfunc (p *Platform) runConnection(ctx context.Context) error {\n\tdev, err := p.client.CreateDevice(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create device: %w\", err)\n\t}\n\tp.mu.Lock()\n\tprevDevice := p.deviceURL\n\tp.deviceURL = dev.URL\n\tp.mu.Unlock()\n\tif prevDevice != \"\" && prevDevice != dev.URL {\n\t\tif err := p.client.DeleteDevice(ctx, prevDevice); err != nil {\n\t\t\tslog.Debug(\"webex: delete stale device failed\", \"error\", err)\n\t\t}\n\t}\n\n\theader := map[string][]string{\"Authorization\": {\"Bearer \" + p.token}}\n\tconn, _, err := websocket.DefaultDialer.DialContext(ctx, dev.WebSocketURL, header)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dial websocket: %s\", core.RedactToken(err.Error(), p.token))\n\t}\n\tdefer func() { _ = conn.Close() }()\n","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/webex/webex.go#L288-L324","documentation":"runConnection registers a Mercury device via the Webex device registration API before opening the WebSocket. If CreateDevice fails (non-2xx or transport error) the error is wrapped as 'create device: %w' and the reconnect loop surfaces it.","triggerScenarios":"POST to the Webex devices endpoint returns 401/403/5xx or the HTTP transport fails (DNS, TLS, timeout) each time the connection is (re)established.","commonSituations":"Token expired mid-run so re-registration fails on reconnect; Webex outage blocking device registration; network restrictions/proxy blocking webexapis.com; rate limiting from aggressive reconnect loops.","solutions":["Refresh the bot token — 401 here means credentials went stale after startup","Check connectivity to webexapis.com (proxy, firewall, DNS)","If this fires repeatedly, add exponential backoff to reconnect attempts to avoid rate limiting","Inspect the wrapped cause for the precise status code and act on it (429 → back off, 403 → scopes)"],"exampleFix":"// before\n// reconnect loop: immediate retry\nfor { runConnection(ctx) }\n// after\nfor {\n    if err := runConnection(ctx); err != nil {\n        backoff = min(backoff*2, 5*time.Minute)\n    }\n    select { case <-ctx.Done(): return; case <-time.After(backoff): }\n}","handlingStrategy":"retry","validationCode":"// ensure token valid and network reachable before the run loop:\n// curl -f -H \"Authorization: Bearer $TOKEN\" https://webexapis.com/v1/people/me","typeGuard":null,"tryCatchPattern":"for {\n    err := runConnection(ctx)\n    if ctx.Err() != nil { return }\n    slog.Warn(\"reconnecting\", \"err\", err)\n    select { case <-ctx.Done(): return; case <-time.After(backoff): }\n    backoff = min(backoff*2, maxBackoff)\n}","preventionTips":["Use exponential backoff on reconnect loops to avoid rate limits","Refresh tokens when approaching expiry so re-registration always succeeds","Monitor 4xx vs 5xx: 4xx needs a credential fix, 5xx just needs patience","Alert when device registration fails repeatedly (sustained outage or auth)"],"tags":["webex","websocket","device-registration","api"],"backgroundTag":"api-request-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}