cloudflare/cloudflared · error
ServeTunnel: %v
Error message
ServeTunnel: %v
What it means
In ServeTunnel's deferred recover handler, a panic value that is not an `error` cannot be wrapped directly, so it is converted with fmt.Errorf("ServeTunnel: %v", r). This preserves the panic payload (string, runtime error, etc.) as an error, then attaches the goroutine stack trace and marks the tunnel failure as recoverable so the supervisor can retry.
Source
Thrown at supervisor/tunnel.go:384
// ServeTunnel runs a single tunnel connection, returns nil on graceful shutdown,
// on error returns a flag indicating if error can be retried
func (e *EdgeTunnelServer) serveTunnel(
ctx context.Context,
connLog *ConnAwareLogger,
addr *allregions.EdgeAddr,
connIndex uint8,
fuse *booleanFuse,
backoff *protocolFallback,
protocol connection.Protocol,
) (err error, recoverable bool) {
// Treat panics as recoverable errors
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("ServeTunnel: %v", r)
}
err = errors.Wrapf(err, "stack trace: %s", string(debug.Stack()))
recoverable = true
}
}()
defer e.config.Observer.SendDisconnect(connIndex)
err, recoverable = e.serveConnection(
ctx,
connLog,
addr,
connIndex,
fuse,
backoff,
protocol,
)
if err != nil {View on GitHub (pinned to 2253eeeb25)
Solutions
- Read the attached 'stack trace: ...' wrapper in the error to locate the panicking frame.
- Fix the underlying panic cause (nil dereference, bad index, concurrent map access) identified by the stack.
- Avoid panic(non-error) in your own code; panic with error values so the recover path preserves types.
- Run with `GOTRACEBACK=all` / race detector in testing to surface the concurrency or memory bug causing the panic.
- Update cloudflared if the stack points into a dependency; the panic may already be fixed upstream.
Defensive patterns
Strategy: retry
Try / catch
err := stream.PipeBidirectional(ctx, tunnelConn, originConn)
if err != nil && strings.Contains(err.Error(), "timeout waiting for second stream") {
log.Warn().Err(err).Msg("second stream never arrived; connection terminated")
// classify as benign for single-stream protocols
} Prevention
- Keep cloudflared and edge protocol versions aligned (upgrade regularly).
- Enable TCP keepalives to reap half-open single-stream connections.
- Tune maxWaitForSecondStream if the proxied application legitimately opens the second stream late.
When it happens
Trigger: Any goroutine panic inside ServeTunnel's call tree that panics with a non-error value: e.g. panic("string literal"), nil map writes (runtime errors are error, but panic(1) or panic(fmt.Sprintf(...)) are not), or library code panicking with arbitrary values.
Common situations: Concurrency bugs (concurrent map read/write panics are runtime errors but can arrive as non-error values depending on throw path), indexing bugs, misconfigured handlers panicking with strings, third-party dependency panics during tunnel serving.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/9a0eff51fcdeb6a5.
Report an issue: GitHub.