{"record":{"id":"dfca89f0144907ad","repo":"JuliusBrussee/caveman","slug":"telemetry-attributes-exceed-d-keys","errorCode":null,"errorMessage":"telemetry attributes exceed %d keys","messagePattern":"telemetry attributes exceed (.+?) keys","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/telemetry/span.go","lineNumber":257,"sourceCode":"\t\t}\n\t}\n\tif strings.Contains(lower, \"api_key\") || strings.Contains(lower, \"access_token\") || strings.Contains(lower, \"refresh_token\") {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// SanitizeAttributes returns a deterministic copy. Sensitive keys are removed;\n// malformed or oversized metadata fails closed so callers can reject the record.\nfunc SanitizeAttributes(attrs map[string]string) (map[string]string, error) {\n\tkeys := make([]string, 0, len(attrs))\n\tfor key := range attrs {\n\t\tif AttributeAllowed(key) {\n\t\t\tkeys = append(keys, key)\n\t\t}\n\t}\n\tif len(keys) > MaxAttributes {\n\t\treturn nil, fmt.Errorf(\"telemetry attributes exceed %d keys\", MaxAttributes)\n\t}\n\tsort.Strings(keys)\n\tout := make(map[string]string, len(keys))\n\ttotal := 0\n\tfor _, key := range keys {\n\t\tvalue := attrs[key]\n\t\tif len(key) > MaxAttributeKeyBytes {\n\t\t\treturn nil, fmt.Errorf(\"telemetry attribute key exceeds %d bytes\", MaxAttributeKeyBytes)\n\t\t}\n\t\tif len(value) > MaxAttributeValueBytes {\n\t\t\treturn nil, fmt.Errorf(\"telemetry attribute %q exceeds %d bytes\", key, MaxAttributeValueBytes)\n\t\t}\n\t\ttotal += len(key) + len(value)\n\t\tif total > MaxAttributesBytes {\n\t\t\treturn nil, fmt.Errorf(\"telemetry attributes exceed %d bytes\", MaxAttributesBytes)\n\t\t}\n\t\tout[key] = value\n\t}","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/telemetry/span.go#L239-L275","documentation":"SanitizeAttributes in shared/platform/telemetry/span.go enforces a hard cap of MaxAttributes keys per span. If, after filtering to allowed attribute keys, more than MaxAttributes remain, it fails closed and returns an error instead of silently truncating — the span parse fails rather than emitting an oversized attribute map.","triggerScenarios":"Calling Parse on span data whose attribute map (after AttributeAllowed filtering) contains more than MaxAttributes keys. Also exercised deliberately by TestSanitizeAttributesFailsClosedOnBounds.","commonSituations":"Instrumentation attaching a key per tag/label on high-cardinality data; merging attributes from multiple sources into one span; a producer bug duplicating keys with different casing.","solutions":["Reduce the number of attributes per span before sending (drop low-value keys, aggregate tags).","Move high-cardinality data out of span attributes into logs or dedicated events.","Batch data across multiple spans instead of one span carrying everything.","Check for producer bugs that generate unbounded key sets."],"exampleFix":"// before\nspan.SetAttributes(map[string]string{ \"user.tag.0\": ..., \"user.tag.1\": ..., /* hundreds */ })\n\n// after\nspan.SetAttributes(map[string]string{\"user.tag.count\": \"212\", \"user.tag.sample\": \"gold\"})","handlingStrategy":"validation","validationCode":"if len(attrs) > maxAttributes /* mirror MaxAttributes constant */ {\n    // drop lowest-priority keys or split into another span before Parse\n    attrs = pruneToTopN(attrs, maxAttributes)\n}","typeGuard":null,"tryCatchPattern":"sanitized, err := telemetry.SanitizeAttributes(raw)\nif err != nil {\n    if strings.Contains(err.Error(), \"exceed\") && strings.Contains(err.Error(), \"keys\") {\n        raw = reduceAttributes(raw)\n        sanitized, err = telemetry.SanitizeAttributes(raw)\n    }\n    if err != nil { return err }\n}","preventionTips":["Cap attribute count at instrumentation time; keep a fixed, small schema of keys.","Route high-cardinality tag data to logs/events instead of span attributes.","Check producer code for loops that append an attribute per item.","Know the limit constant and assert against it in tests."],"tags":["telemetry","attributes","limits","go"],"backgroundTag":"payload-too-large","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}