derailed/k9s · error
failed to maintain log stream after %d retries
Error message
failed to maintain log stream after %d retries
What it means
The tailing goroutine behind Pod log streaming retries the request with exponential backoff, stopping early only when the pod is detected terminating/deleted. When the retry budget (logRetryCount) is exhausted without a stable stream, it pushes this error into the channel as an ErrLogItem rather than hanging or panicking.
Source
Thrown at internal/dao/pod.go:465
case <-ctx.Done():
return
case <-time.After(delay):
if delay = backoffCtx.NextBackOff(); delay == backoff.Stop {
return
}
}
continue
case streamCanceled:
return
}
// Reset backoff and delay on successful connection
bf.Reset()
delay = logBackoffInitial
}
// Out of retries
out <- opts.ToErrLogItem(fmt.Errorf("failed to maintain log stream after %d retries", logRetryCount))
}()
go func() {
wg.Wait()
close(out)
}()
return out
}
func readLogs(ctx context.Context, stream io.ReadCloser, out chan<- *LogItem, opts *LogOptions) streamResult {
defer func() {
if err := stream.Close(); err != nil && !errors.Is(err, io.ErrClosedPipe) {
slog.Error("Failed to close stream",
slogs.Container, opts.Info(),
slogs.Error, err,
)
}View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Check connectivity to the API server, then restart the log tail/view
- Verify the pod still exists and is not stuck Terminating: kubectl get pod <p> -o wide
- Stabilize the path (VPN, LB idle timeouts > read interval) instead of only retrying harder
Defensive patterns
Strategy: retry
Validate before calling
// Before opening a long tail, confirm the pod exists and the API answers
if podDAO.shouldStopRetrying(path) { return nil }
if _, err := podDAO.GetInstance(fqn); err != nil { return err } Try / catch
for item := range podDAO.Logs(ctx, opts) {
if item.IsErr && strings.Contains(item.Message, "failed to maintain log stream") {
// re-open the stream after a pause; stop after N user-level retries
}
} Prevention
- Reopen log tails on visibility/rotation events instead of leaving dead streams
- Keep-alive or shorten idle windows on VPN/LB paths that kill long HTTP streams
- Check pod liveness (kubectl get pod) before blaming the network when tails die
When it happens
Trigger: The API-server log endpoint stays unreachable for the entire backoff window: network drops, API server restarts, proxy/LB idle timeouts killing streams, or a pod stuck Terminating that shouldStopRetrying does not recognize.
Common situations: Laptop sleep/resume with a log tail open; VPN or relay to the cluster dropping; heavily loaded control planes timing out on log requests; long-running tails behind aggressive load balancers.
Related errors
- stream closed: %w for %s
- dialLogs - no connection to dial
- expecting a context factory
- expecting a job resource
- no factory in context
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/73d1c016a27e3986.
Report an issue: GitHub.