tailscale/tailscale · error
%v: %v
Error message
%v: %v
What it means
The inner wrapper in derphttp's dial path: when a connect attempt fails AND the passed context is already done, the error is rewrapped as "<ctx.Err()>: <original err>". So this message means the context was canceled or its deadline was exceeded while dialing; the text after the colon is what the dial itself reported. The outer layer then adds "%s connect to %v: ...".
Source
Thrown at derp/derphttp/derphttp_client.go:387
cancel()
}
}()
defer cancel()
var reg *tailcfg.DERPRegion // nil when using c.url to dial
if c.getRegion != nil {
reg = c.getRegion()
if reg == nil {
return nil, 0, errors.New("DERP region not available")
}
}
var tcpConn net.Conn
defer func() {
if err != nil {
if ctx.Err() != nil {
err = fmt.Errorf("%v: %v", ctx.Err(), err)
}
err = fmt.Errorf("%s connect to %v: %v", caller, c.targetString(reg), err)
if tcpConn != nil {
go tcpConn.Close()
}
}
}()
var node *tailcfg.DERPNode // nil when using c.url to dial
var idealNodeInRegion bool
switch {
case canWebsockets && useWebsockets():
var urlStr string
if c.url != nil {
urlStr = c.url.String()
} else {
urlStr = c.urlString(reg.Nodes[0])
}View on GitHub (pinned to 6e0912f979)
Solutions
- Read the message: 'context deadline exceeded' first means the timeout was too small — measure handshake time and enlarge the deadline.
- 'context canceled' means your own code closed/canceled — make sure you don't Close the client while Connect is pending.
- If the underlying dial error is a network fault, fix reachability first; the ctx wrap only tells you the attempt was aborted.
Defensive patterns
Strategy: retry
Type guard
// isCtxCanceled reports whether the dial failed because ctx was done
// (deadline exceeded or canceled) rather than a network fault.
func isCtxCanceled(ctx context.Context, err error) bool {
return ctx.Err() != nil && err != nil && strings.Contains(err.Error(), ctx.Err().Error())
} Try / catch
conn, err := dialDERP(ctx)
if err != nil {
if isCtxCanceled(ctx, err) {
// Aborted by our own deadline/cancel: retry with a longer budget
// unless the parent is shutting down.
if ctx.Err() == context.Canceled {
return err // shutdown; do not retry
}
ctx, cancel = context.WithTimeout(parent, 3*timeout)
defer cancel()
conn, err = dialDERP(ctx)
}
} Prevention
- Size connect deadlines for worst-case TCP+TLS handshake (add headroom on lossy links), not the happy path.
- Never Close/Cancel the parent client while a Connect is in flight; sequence shutdown after dial completion.
- Distinguish context.Canceled (shut down, stop) from context.DeadlineExceeded (retry with more budget) in retry logic.
When it happens
Trigger: dialNode/dialRegion exceeding the caller's ctx deadline (tight timeout, slow network), or the parent context being canceled mid-dial (client Close, shutdown, region switch).
Common situations: Context deadline set shorter than TCP+TLS handshake time on lossy links; the DERP client closed while a background connect was in flight; prober cancellations.
Related errors
- connection attempts aborted by context: %w
- dial of %v: %v
- timeout: %w
- register request: %w
- fetching server key: %w
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/fc266e3fe39b6138.
Report an issue: GitHub.