{"record":{"id":"d89b916dcb2aad53","repo":"cloudflare/cloudflared","slug":"cause","errorCode":null,"errorMessage":"cause","messagePattern":"cause","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tunnelrpc/pogs/registration_server.go","lineNumber":179,"sourceCode":"\t\t}\n\t\treturn nil\n\t})\n\tresponse, err := promise.Result().Struct()\n\tif err != nil {\n\t\treturn nil, wrapRPCError(err)\n\t}\n\tresult := response.Result()\n\tswitch result.Which() {\n\tcase proto.ConnectionResponse_result_Which_error:\n\t\tresultError, err := result.Error()\n\t\tif err != nil {\n\t\t\treturn nil, wrapRPCError(err)\n\t\t}\n\t\tcause, err := resultError.Cause()\n\t\tif err != nil {\n\t\t\treturn nil, wrapRPCError(err)\n\t\t}\n\t\terr = errors.New(cause)\n\t\tif resultError.ShouldRetry() {\n\t\t\terr = RetryErrorAfter(err, time.Duration(resultError.RetryAfter()))\n\t\t}\n\t\treturn nil, err\n\n\tcase proto.ConnectionResponse_result_Which_connectionDetails:\n\t\tconnDetails, err := result.ConnectionDetails()\n\t\tif err != nil {\n\t\t\treturn nil, wrapRPCError(err)\n\t\t}\n\t\tdetails := new(ConnectionDetails)\n\t\tif err = details.UnmarshalCapnproto(connDetails); err != nil {\n\t\t\treturn nil, wrapRPCError(err)\n\t\t}\n\t\treturn details, nil\n\t}\n\n\treturn nil, newRPCError(\"unknown result which %d\", result.Which())","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/tunnelrpc/pogs/registration_server.go#L161-L197","documentation":"In tunnelrpc/pogs/registration_server.go, after a remote RegisterConnection RPC call, the server-side result may carry a structured RegisterConnectionError. The client extracts its human-readable Cause() string and rebuilds a plain Go error from it, optionally wrapping it in a RetryErrorAfter when the remote says the error is retryable after some duration. This is the deserialized error reported by the Cloudflare edge, not a local failure.","triggerScenarios":"RegisterConnection receives a ConnectionResponse whose result is a RegisterConnectionError variant: resultError.Cause() yields a non-empty message, which is converted via errors.New(cause). If ShouldRetry() is true, the error is wrapped with RetryTimeUntil using resultError.RetryAfter().","commonSituations":"Edge rejects a tunnel connection: duplicate connection with the same connection ID, tunnel not found / credentials mismatch, too many connections for the tunnel, or the tunnel is being migrated/deprecated so the edge asks the client to retry later.","solutions":["Read the returned cause message — it states the edge-side reason (e.g. duplicate connection, bad credentials) and fix that root cause.","If the error wraps RetryErrorAfter (ShouldRetry), wait at least the RetryAfter duration before reconnecting.","Verify tunnel credentials (TUNNEL_TOKEN or cert.pem/ID+secret) match an existing, non-deleted tunnel.","Ensure old cloudflared processes holding the same connection ID are terminated before restarting.","Update cloudflared if the edge reports protocol/version incompatibility."],"exampleFix":"// before: treat every failure as fatal\nconn, err := registrationClient.RegisterConnection(ctx, ...)\nif err != nil {\n\treturn err\n}\n// after: honor retryable edge errors\nvar retry RetryError\nif errors.As(err, &retry) {\n\ttime.Sleep(retry.RetryAfter())\n\tcontinue // re-attempt registration\n}\nreturn err","handlingStrategy":"try-catch","validationCode":"if tunnelID == \"\" || credentials == nil {\n    return errors.New(\"tunnel ID and credentials required before RegisterConnection\")\n}","typeGuard":"var retry RetryError\nisRetryable := errors.As(err, &retry)","tryCatchPattern":"conn, err := client.RegisterConnection(ctx, auth, options)\nif err != nil {\n    var retry RetryError\n    if errors.As(err, &retry) {\n        select {\n        case <-time.After(retry.RetryAfter()):\n            // re-attempt registration\n        case <-ctx.Done():\n            return ctx.Err()\n        }\n    }\n    return fmt.Errorf(\"register connection: %w\", err)\n}","preventionTips":["Terminate stale cloudflared processes before re-registering the same connection ID","Validate tunnel credentials/token before dialing the edge","Honor RetryAfter from RetryError instead of tight-loop reconnects","Keep cloudflared updated to avoid edge-side protocol rejections"],"tags":["rpc","tunnel","remote-error","retry"],"backgroundTag":"upstream-api-error","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}