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

  1. Read the returned cause message — it states the edge-side reason (e.g. duplicate connection, bad credentials) and fix that root cause.
  2. If the error wraps RetryErrorAfter (ShouldRetry), wait at least the RetryAfter duration before reconnecting.
  3. Verify tunnel credentials (TUNNEL_TOKEN or cert.pem/ID+secret) match an existing, non-deleted tunnel.
  4. Ensure old cloudflared processes holding the same connection ID are terminated before restarting.
  5. 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

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.