{"record":{"id":"73d1c016a27e3986","repo":"derailed/k9s","slug":"failed-to-maintain-log-stream-after-d-retries","errorCode":null,"errorMessage":"failed to maintain log stream after %d retries","messagePattern":"failed to maintain log stream after (.+?) retries","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dao/pod.go","lineNumber":465,"sourceCode":"\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn\n\t\t\t\tcase <-time.After(delay):\n\t\t\t\t\tif delay = backoffCtx.NextBackOff(); delay == backoff.Stop {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\tcase streamCanceled:\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Reset backoff and delay on successful connection\n\t\t\tbf.Reset()\n\t\t\tdelay = logBackoffInitial\n\t\t}\n\n\t\t// Out of retries\n\t\tout <- opts.ToErrLogItem(fmt.Errorf(\"failed to maintain log stream after %d retries\", logRetryCount))\n\t}()\n\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(out)\n\t}()\n\n\treturn out\n}\n\nfunc readLogs(ctx context.Context, stream io.ReadCloser, out chan<- *LogItem, opts *LogOptions) streamResult {\n\tdefer func() {\n\t\tif err := stream.Close(); err != nil && !errors.Is(err, io.ErrClosedPipe) {\n\t\t\tslog.Error(\"Failed to close stream\",\n\t\t\t\tslogs.Container, opts.Info(),\n\t\t\t\tslogs.Error, err,\n\t\t\t)\n\t\t}","sourceCodeStart":447,"sourceCodeEnd":483,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/dao/pod.go#L447-L483","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Before opening a long tail, confirm the pod exists and the API answers\nif podDAO.shouldStopRetrying(path) { return nil }\nif _, err := podDAO.GetInstance(fqn); err != nil { return err }","typeGuard":null,"tryCatchPattern":"for item := range podDAO.Logs(ctx, opts) {\n    if item.IsErr && strings.Contains(item.Message, \"failed to maintain log stream\") {\n        // re-open the stream after a pause; stop after N user-level retries\n    }\n}","preventionTips":["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"],"tags":["kubernetes","logs","stream","retry","network"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}