{"record":{"id":"168a2559bd719ab0","repo":"JuliusBrussee/caveman","slug":"option-not-found","errorCode":null,"errorMessage":"option not found","messagePattern":"option not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"browse/cdp.go","lineNumber":207,"sourceCode":"\t\t})); err != nil {\n\t\t\treturn ActionResult{OK: false, Settled: false}, err\n\t\t}\n\t\treturn dispatchedAction(), nil\n\tcase \"select\":\n\t\tif err := d.scrollIntoView(runCtx, target); err != nil {\n\t\t\treturn ActionResult{OK: false, Settled: false}, err\n\t\t}\n\t\tif _, _, err := d.waitActionable(runCtx, target); err != nil {\n\t\t\treturn ActionResult{OK: false, Settled: false}, err\n\t\t}\n\t\toption := req.Option\n\t\tif option == \"\" {\n\t\t\toption = req.Text\n\t\t}\n\t\tif option == \"\" {\n\t\t\treturn ActionResult{OK: false, Settled: false}, errors.New(\"select: missing option\")\n\t\t}\n\t\tif err := d.callOnNode(runCtx, target, fmt.Sprintf(`function(){ const wanted = %s; const options = Array.from(this.options || []); const match = options.find(o => o.value === wanted || o.label === wanted || o.textContent.trim() === wanted); if (!match) { throw new Error(\"option not found\"); } this.value = match.value; this.dispatchEvent(new Event(\"input\", {bubbles:true})); this.dispatchEvent(new Event(\"change\", {bubbles:true})); return true; }`, jsString(option))); err != nil {\n\t\t\treturn ActionResult{OK: false, Settled: false}, err\n\t\t}\n\t\treturn dispatchedAction(), nil\n\tcase \"scroll\":\n\t\tif err := d.scrollIntoView(runCtx, target); err != nil {\n\t\t\treturn ActionResult{OK: false, Settled: false}, err\n\t\t}\n\t\tif _, _, err := d.waitActionable(runCtx, target); err != nil {\n\t\t\treturn ActionResult{OK: false, Settled: false}, err\n\t\t}\n\t\treturn dispatchedAction(), nil\n\tdefault:\n\t\treturn ActionResult{OK: false, Settled: false}, fmt.Errorf(\"unknown action: %s\", req.Action)\n\t}\n}\n\nfunc dispatchedAction() ActionResult {\n\t// CDP acknowledged dispatch, but asynchronous application state may still be","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/browse/cdp.go#L189-L225","documentation":"SanitizeAttributes in shared/platform/telemetry/span.go rejects an attribute map once more than MaxAttributes (128) keys survive the allowlist filter. Telemetry pipelines cap attribute count so spans stay bounded and downstream exporters do not reject or drop oversized records. The function fails closed: the error tells the caller to trim the map rather than silently truncating it.","triggerScenarios":"Calling SanitizeAttributes(attrs) (or a span-recording API that routes through it) with a map where more than 128 distinct keys pass AttributeAllowed. Only allowlisted keys count toward the limit, so a large map of disallowed keys does not trigger it, but 129+ allowed keys does.","commonSituations":"Attaching per-request dimensions dynamically (user id, trace flags, feature flags) until the map grows past 128; bulk-forwarding upstream headers or labels into span attributes; a loop that accumulates attributes across retries without resetting the map.","solutions":["Count allowlisted keys before building the span: if more than 128 survive, keep only the highest-priority ones (trace id, route, status).","Hoist static/repeated attributes into a shared resource or logger configuration instead of per-span attributes.","Raise MaxAttributes in shared/platform/telemetry/span.go:39 only if your exporter supports it, and re-run telemetry tests."],"exampleFix":"// before\nattrs := map[string]string{}\nfor k, v := range requestHeaders {\n    attrs[k] = v // unbounded growth\n}\nspan := tracer.Start(ctx, attrs)\n\n// after\nattrs := map[string]string{}\nfor k, v := range requestHeaders {\n    if len(attrs) >= telemetry.MaxAttributes {\n        break\n    }\n    attrs[k] = v\n}\nspan := tracer.Start(ctx, attrs)","handlingStrategy":"validation","validationCode":"func countAllowedAttrs(attrs map[string]string) int {\n    n := 0\n    for k := range attrs {\n        if telemetry.AttributeAllowed(k) {\n            n++\n        }\n    }\n    return n\n}\n\n// before span creation:\nif countAllowedAttrs(attrs) > telemetry.MaxAttributes {\n    attrs = pruneAttrs(attrs, telemetry.MaxAttributes) // keep priority keys\n}","typeGuard":"func attrsWithinKeyCount(attrs map[string]string) bool {\n    return countAllowedAttrs(attrs) <= telemetry.MaxAttributes\n}","tryCatchPattern":null,"preventionTips":["Build span attributes from a fixed, reviewed set of keys instead of copying arbitrary maps.","Run telemetry unit tests that assert attribute count stays under 128 for every instrumented call path.","Log-and-drop instead of propagate when SanitizeAttributes errors: sanitization failures should degrade telemetry, not the request."],"tags":["telemetry","limits","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}