diff --git a/model/labels/labels_stringlabels.go b/model/labels/labels_stringlabels.go index c9be42bf74..5789815789 100644 --- a/model/labels/labels_stringlabels.go +++ b/model/labels/labels_stringlabels.go @@ -16,6 +16,7 @@ package labels import ( + "errors" "slices" "strings" "unsafe" @@ -540,13 +541,16 @@ func marshalLabelToSizedBuffer(m *Label, data []byte) int { return len(data) - i } +// ErrLabelTooLong is returned when a label name or value exceeds the maximum allowed size. +var ErrLabelTooLong = errors.New("string too long to encode as label") + func sizeWhenEncoded(x uint64) (n int) { if x < 255 { return 1 } else if x <= 1<<24 { return 4 } - panic("String too long to encode as label.") + panic(ErrLabelTooLong) } func encodeSize(data []byte, offset, v int) int { diff --git a/promql/parser/parse.go b/promql/parser/parse.go index cefc627fda..6358b27c36 100644 --- a/promql/parser/parse.go +++ b/promql/parser/parse.go @@ -340,16 +340,23 @@ var errUnexpected = errors.New("unexpected error") // recover is the handler that turns panics into returns from the top level of Parse. func (*parser) recover(errp *error) { e := recover() - switch _, ok := e.(runtime.Error); { - case ok: + if e == nil { + return + } + + switch err := e.(type) { + case runtime.Error: // Print the stack trace but do not inhibit the running application. buf := make([]byte, 64<<10) buf = buf[:runtime.Stack(buf, false)] fmt.Fprintf(os.Stderr, "parser panic: %v\n%s", e, buf) *errp = errUnexpected - case e != nil: - *errp = e.(error) + case error: + *errp = err + default: + // Handle non-error panic values (e.g., strings) gracefully. + *errp = fmt.Errorf("%v", e) } } diff --git a/promql/parser/parse_test.go b/promql/parser/parse_test.go index ab5564f0ff..856049dea9 100644 --- a/promql/parser/parse_test.go +++ b/promql/parser/parse_test.go @@ -6004,6 +6004,28 @@ func TestRecoverParserError(t *testing.T) { panic(e) } +func TestRecoverParserString(t *testing.T) { + p := NewParser("foo bar") + var err error + + defer func() { + require.Error(t, err) + require.EqualError(t, err, "some string panic") + }() + defer p.recover(&err) + + panic("some string panic") +} + +func TestParseLabelTooLong(t *testing.T) { + const maxLabelSize = 1 << 24 + longValue := strings.Repeat("a", maxLabelSize+1) + input := `{job="` + longValue + `"}` + + _, err := ParseMetric(input) + require.Error(t, err) +} + func TestExtractSelectors(t *testing.T) { for _, tc := range [...]struct { input string