jaegertracing/jaeger · error
websocket dial %s: %w
Error message
websocket dial %s: %w
What it means
DialWsAdapter establishes the WebSocket connection to an AI agent; when the dialer returns an error (or the upgrade response has a non-101 status) it wraps it as "websocket dial <url>: <cause>". When an HTTP error response is received it also logs status and body at the point of failure for diagnosis.
Source
Thrown at cmd/jaeger/internal/extension/jaegerquery/internal/jaegerai/ws_adapter.go:72
var header http.Header
if len(headers) > 0 {
header = make(http.Header, len(headers))
for name, value := range headers.Iter {
header.Set(name, string(value))
}
}
conn, resp, err := dialer.DialContext(ctx, url, header) //nolint:bodyclose // gorilla wraps resp.Body in io.NopCloser; no close needed
if err != nil {
if resp != nil {
body, _ := io.ReadAll(resp.Body)
logger.Error(
"WebSocket dial failed",
zap.Int("status", resp.StatusCode),
zap.String("body", string(body)),
zap.Error(err),
)
}
return nil, fmt.Errorf("websocket dial %s: %w", url, err)
}
return &WsReadWriteCloser{conn: conn, logger: logger}, nil
}
func (w *WsReadWriteCloser) Read(p []byte) (int, error) {
if len(p) == 0 {
return 0, nil
}
for {
if w.r == nil {
// If the last seen message lacked a delimiter, return the newline
// before opening the next reader.
if w.messageUnterminated {
w.messageUnterminated = false
p[0] = '\n'
return 1, nil
}
_, r, err := w.conn.NextReader()View on GitHub (pinned to 806f444784)
Solutions
- Read the wrapped cause and the 'WebSocket dial failed' log (status + body) to identify whether it is DNS, connection refused, TLS, or an HTTP error status.
- Correct the agent URL in configuration (scheme/host/port/path).
- Supply the required agent_headers for authenticated agents.
- Verify network reachability and that proxies allow WebSocket upgrades.
Defensive patterns
Strategy: retry
Validate before calling
u, err := url.Parse(agentURL)
if err != nil || (u.Scheme != "ws" && u.Scheme != "wss") || u.Host == "" {
return fmt.Errorf("bad agent URL: %q", agentURL)
}
for _, h := range agentHeaders {
if strings.TrimSpace(h.Name) == "" { return fmt.Errorf("blank header name") }
} Try / catch
conn, err := jaegerai.DialWsAdapter(ctx, url, headers, logger)
if err != nil {
var httpErr interface{ StatusCode() int } // inspect logged status/body for 401/403 vs network errors
if strings.Contains(err.Error(), "websocket dial") {
logger.Error("agent websocket unreachable", zap.Error(err))
}
return err
} Prevention
- Pre-flight check agent reachability (TCP/HTTP) before dialing.
- Configure auth headers (e.g. Authorization) for agents that require them.
- Use wss:// with valid certificates; test through any corporate proxy.
- Apply exponential backoff on dial retries to avoid hammering a down agent.
When it happens
Trigger: Any call path (AI request handling, health checks) where the agent endpoint is unreachable, DNS fails, TLS fails, or the server answers the upgrade with 4xx/5xx instead of switching protocols.
Common situations: Agent service down or misconfigured URL; auth headers missing so the agent returns 401 on upgrade; corporate proxy blocking WebSocket upgrades; wss:// to a host with an invalid certificate.
Related errors
- dial: %w
- failed to query trace IDs: %w
- cannot start the admin server: %w
- invalid HTTP server host:port: %w
- invalid gRPC server host:port: %w
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/736f62f2663172b1.
Report an issue: GitHub.