{"record":{"id":"765f16801017bfaf","repo":"thanos-io/thanos","slug":"panic-v-in-server-iterator-s","errorCode":null,"errorMessage":"panic %v in server iterator: %s","messagePattern":"panic (.+?) in server iterator: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/store/storepb/inprocess.go","lineNumber":147,"sourceCode":"}\n\nfunc (r *readOnlySeriesClient) SendMsg(m interface{}) error {\n\treturn nil\n}\n\nfunc (r *readOnlySeriesClient) RecvMsg(m interface{}) error {\n\treturn io.EOF\n}\n\nfunc (s serverAsClient) Series(ctx context.Context, in *SeriesRequest, _ ...grpc.CallOption) (Store_SeriesClient, error) {\n\tif s.readOnly.Load() {\n\t\treturn &readOnlySeriesClient{ctx: ctx}, nil\n\t}\n\tvar srvIter iter.Seq2[*SeriesResponse, error] = func(yield func(*SeriesResponse, error) bool) {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tst := debug.Stack()\n\t\t\t\tpanic(fmt.Sprintf(\"panic %v in server iterator: %s\", r, st))\n\t\t\t}\n\t\t}()\n\t\tsrv := newInProcessServer(ctx, yield)\n\t\terr := s.srv.Series(in, srv)\n\t\tif err != nil {\n\t\t\tyield(nil, err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tclientIter, stop := iter.Pull2(srvIter)\n\treturn newInProcessClient(ctx, clientIter, stop), nil\n}\n","sourceCodeStart":129,"sourceCodeEnd":161,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/store/storepb/inprocess.go#L129-L161","documentation":"The server-side iterator sequence (iter.Seq2) for in-process Series calls recovers from any panic raised while yielding SeriesResponses and re-panics with the original value plus a stack trace, prefixed 'panic %v in server iterator'. This converts an opaque panic into one that identifies it came from the store server's streaming iterator.","triggerScenarios":"Any panic inside the series streaming callback — e.g. nil pointer while building SeriesResponse, index out of range in chunk conversion, or a panic in srv.Series — while consuming the iterator returned by the in-process store client.","commonSituations":"Upstream storepb server implementation panics on malformed or unexpected series data; concurrent map access or nil logger in the server; a bug triggered by specific series/chunk shapes during StoreAPI iteration.","solutions":["Read the embedded stack trace in the panic message to find the panicking frame in the Series server path","Fix the underlying panic cause in the server iterator (nil guard, bounds check)","Update Thanos/pinnacle of the in-process store to a version with the panic fix","Wrap consumer-side iteration with recover only as a last-resort safety net"],"exampleFix":"// before: panics propagate cryptically\nfor s, err := range client.Series(ctx, req) { ... }\n// after: recover and log full stack\nfunc safeIter() { defer func(){ if r := recover(); r != nil { log.Errorf(\"series iter panic: %v\\n%s\", r, debug.Stack()) } }(); for s, err := range client.Series(ctx, req) { _ = s; _ = err } }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"defer func() { if r := recover(); r != nil { log.Errorf(\"series iter panic: %v\\n%s\", r, debug.Stack()) } }()","preventionTips":["Run the store server with race detector in tests","Nil-check all fields used when building SeriesResponse","Upgrade to releases with known iterator panics fixed"],"tags":["panic","goroutine","iterator","storepb"],"backgroundTag":"panic-in-server-iterator","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}