cloudflare/cloudflared · error
cause
Error message
cause
What it means
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.
Source
Thrown at tunnelrpc/pogs/registration_server.go:179
}
return nil
})
response, err := promise.Result().Struct()
if err != nil {
return nil, wrapRPCError(err)
}
result := response.Result()
switch result.Which() {
case proto.ConnectionResponse_result_Which_error:
resultError, err := result.Error()
if err != nil {
return nil, wrapRPCError(err)
}
cause, err := resultError.Cause()
if err != nil {
return nil, wrapRPCError(err)
}
err = errors.New(cause)
if resultError.ShouldRetry() {
err = RetryErrorAfter(err, time.Duration(resultError.RetryAfter()))
}
return nil, err
case proto.ConnectionResponse_result_Which_connectionDetails:
connDetails, err := result.ConnectionDetails()
if err != nil {
return nil, wrapRPCError(err)
}
details := new(ConnectionDetails)
if err = details.UnmarshalCapnproto(connDetails); err != nil {
return nil, wrapRPCError(err)
}
return details, nil
}
return nil, newRPCError("unknown result which %d", result.Which())View on GitHub (pinned to 2253eeeb25)
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.
Example fix
// before: treat every failure as fatal
conn, err := registrationClient.RegisterConnection(ctx, ...)
if err != nil {
return err
}
// after: honor retryable edge errors
var retry RetryError
if errors.As(err, &retry) {
time.Sleep(retry.RetryAfter())
continue // re-attempt registration
}
return err Defensive patterns
Strategy: try-catch
Validate before calling
if tunnelID == "" || credentials == nil {
return errors.New("tunnel ID and credentials required before RegisterConnection")
} Type guard
var retry RetryError isRetryable := errors.As(err, &retry)
Try / catch
conn, err := client.RegisterConnection(ctx, auth, options)
if err != nil {
var retry RetryError
if errors.As(err, &retry) {
select {
case <-time.After(retry.RetryAfter()):
// re-attempt registration
case <-ctx.Done():
return ctx.Err()
}
}
return fmt.Errorf("register connection: %w", err)
} Prevention
- 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
When it happens
Trigger: 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().
Common situations: 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.
Related errors
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/d89b916dcb2aad53.
Report an issue: GitHub.