From a55eb17df341bdde53216257f63787f9e643bde1 Mon Sep 17 00:00:00 2001 From: Kevin DeLoach Date: Fri, 13 Mar 2026 13:00:37 -0400 Subject: [PATCH] fix: panic with kubectl when timestamps are enabled (#7947) Fixes a bug where using the `--timestamps` flag with `kubectl` deploy panics with: > error:panic: runtime error: slice bounds out of range With timestamps enabled, `skaffoldWriter.Write` includes the length of the timestamp in its return value. But `Writer` implementations should return the number of bytes _consumed_, not written. This results in `textio.PrefixWriter`[^1] writing `N` bytes to its buffer and then discarding `N+len(timestamp)` bytes, causing a panic. [^1]: Used by `pkg/skaffold/deploy/kubectl/kubectl.go` Relevant `textio.PrefixWriter` source code (ref: https://github.com/segmentio/textio/blob/master/prefix.go#L54-L55): ```go c, err = w.writeLine(chunk) w.discard(c) ``` Fixes: https://github.com/GoogleContainerTools/skaffold/issues/7947 --- pkg/skaffold/output/output.go | 15 ++++++--------- pkg/skaffold/output/output_test.go | 10 ++++++++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/pkg/skaffold/output/output.go b/pkg/skaffold/output/output.go index d3a3806a263..79969dbfeb9 100644 --- a/pkg/skaffold/output/output.go +++ b/pkg/skaffold/output/output.go @@ -38,14 +38,13 @@ type skaffoldWriter struct { } func (s skaffoldWriter) Write(p []byte) (int, error) { - written := 0 + if len(p) == 0 { + return 0, nil + } if s.timestamps { - t, err := s.MainWriter.Write([]byte(time.Now().Format(timestampFormat) + " ")) - if err != nil { - return t, err + if _, err := io.WriteString(s.MainWriter, time.Now().Format(timestampFormat+" ")); err != nil { + return 0, err } - - written += t } n, err := s.MainWriter.Write(p) @@ -56,11 +55,9 @@ func (s skaffoldWriter) Write(p []byte) (int, error) { return n, io.ErrShortWrite } - written += n - s.EventWriter.Write(p) - return written, nil + return n, nil } func GetWriter(ctx context.Context, out io.Writer, defaultColor int, forceColors bool, timestamps bool) io.Writer { diff --git a/pkg/skaffold/output/output_test.go b/pkg/skaffold/output/output_test.go index 451d0b64b49..5c62b4d8f5f 100644 --- a/pkg/skaffold/output/output_test.go +++ b/pkg/skaffold/output/output_test.go @@ -163,11 +163,17 @@ func TestWriteWithTimeStamps(t *testing.T) { } for _, test := range tests { - t.Run(test.name, func(t *testing.T) { + testutil.Run(t, test.name, func(t *testutil.T) { var buf bytes.Buffer out := test.writer(&buf) Default.Fprintf(out, "testing!") - testutil.CheckDeepEqual(t, test.expectedLen, len(buf.String())) + t.CheckDeepEqual(test.expectedLen, len(buf.String())) + + // Consume same number of bytes regardless of writer options. + out2 := test.writer(io.Discard) + n, err := out2.Write([]byte("testing!")) + t.CheckNoError(err) + t.CheckDeepEqual(len("testing!"), n) }) } }