jaegertracing/jaeger · error
aggregation bucket has a non-string key: %w
Error message
aggregation bucket has a non-string key: %w
What it means
When decoding aggregation response buckets, Jaeger expects each bucket's 'key' to be a JSON string (aggregations are on traceID or serviceName). UnmarshalJSON fails the decode with this wrapped error if 'key' is present but not a string, instead of yielding an empty key that callers would treat as a valid ID.
Source
Thrown at internal/storage/elasticsearch/esclient/aggregation.go:122
// and any nested sub-aggregations (reached through the promoted Aggregations
// accessors).
type AggregationBucket struct {
Key string
DocCount int
Aggregations
}
func (b *AggregationBucket) UnmarshalJSON(data []byte) error {
raw := map[string]json.RawMessage{}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
// Bucket keys are strings for the fields we aggregate (traceID, serviceName).
// A present-but-non-string key means a mapping regression, so fail the decode
// rather than silently yield an empty key that callers treat as a valid ID.
if k, ok := raw["key"]; ok {
if err := json.Unmarshal(k, &b.Key); err != nil {
return fmt.Errorf("aggregation bucket has a non-string key: %w", err)
}
delete(raw, "key")
}
if dc, ok := raw["doc_count"]; ok {
if err := json.Unmarshal(dc, &b.DocCount); err != nil {
return err
}
delete(raw, "doc_count")
}
b.Aggregations = raw
return nil
}
func (b AggregationBucket) MarshalJSON() ([]byte, error) {
return marshalBucket(b.Key, b.DocCount, b.Aggregations)
}
// HistogramResult holds the buckets of a date_histogram aggregation.View on GitHub (pinned to 806f444784)
Solutions
- Fix the index mapping so the aggregated field is a keyword/string type
- Reindex affected indices so bucket keys are strings
- Check which field the aggregation targets and that the backend version emits string keys
Example fix
// before (mapping)
"serviceVersion": { "type": "long" }
// after
"serviceVersion": { "type": "keyword" } Defensive patterns
Strategy: try-catch
Validate before calling
// before decoding, sanity-check mapping of the aggregated field var m map[string]any json.Unmarshal(mappingJSON, &m) // ensure the aggregated field type is 'keyword', not numeric
Type guard
func isStringKey(raw json.RawMessage) bool {
var s string
return json.Unmarshal(raw, &s) == nil
} Try / catch
var buckets []aggregationBucket
if err := json.Unmarshal(respBody, &buckets); err != nil {
if strings.Contains(err.Error(), "non-string key") {
// alert: index mapping regression on the aggregated field
}
return fmt.Errorf("decoding aggregation response: %w", err)
} Prevention
- Keep aggregated fields mapped as keyword in ES templates
- Add integration tests asserting bucket key types
- Monitor for mapping conflicts after index template changes
When it happens
Trigger: Calling json.Unmarshal/Decode on an aggregation response whose bucket objects contain a non-string 'key' (e.g. a number or object) while unmarshalling into aggregationBucket.
Common situations: Elasticsearch mapping regression so the aggregated field is indexed as a number/keyword-object; querying an unexpected field where keys are numeric.
Related errors
- could not find aggregation of traceIDs
- could not find aggregation of
- failed to convert aggregations to metrics: %w
- %s aggregation not found
- failed to process bucket: %w
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/d512407bad35052a.
Report an issue: GitHub.