{"record":{"id":"c88d75d721e205e1","repo":"JuliusBrussee/caveman","slug":"empty-a11y-recovery-metadata","errorCode":null,"errorMessage":"empty a11y recovery metadata","messagePattern":"empty a11y recovery metadata","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"browse/session.go","lineNumber":213,"sourceCode":"\ts.replaceTargets(targets)\n\treturn mcp.ToolRawText(text)\n}\n\nfunc snapshotTimeout(wait time.Duration) time.Duration {\n\tbase := 15 * time.Second\n\tif wait > 0 {\n\t\tbase += wait\n\t}\n\treturn base\n}\n\nfunc (s *Session) targetsForHandle(handle string) (map[string]Target, error) {\n\tmeta, err := s.eng.RetrieveMetadata(handle)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(meta) == 0 {\n\t\treturn nil, errors.New(\"empty a11y recovery metadata\")\n\t}\n\tvar decoded struct {\n\t\tUIDs map[string]Target `json:\"uids\"`\n\t}\n\tif err := json.Unmarshal(meta, &decoded); err != nil {\n\t\treturn nil, err\n\t}\n\tif decoded.UIDs == nil {\n\t\tdecoded.UIDs = map[string]Target{}\n\t}\n\treturn decoded.UIDs, nil\n}\n\nfunc finalizeSnapshotPayload(payload snapshotPayload) (snapshotPayload, string) {\n\tcounter := tokens.Default()\n\tfor range 8 {\n\t\tencoded, _ := json.Marshal(payload)\n\t\tdelivered := counter.Count(encoded)","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/browse/session.go#L195-L231","documentation":"Returned by Session.targetsForHandle() when s.eng.RetrieveMetadata(handle) succeeds but returns zero bytes for the a11y recovery handle. The metadata blob is where the compressed snapshot stores the uid -> Target map; empty bytes means the handle exists but its payload was never written (or was truncated), so there is no uid map to decode and the act path fails rather than acting on fabricated targets.","triggerScenarios":"Calling an act/query tool with a recovery handle whose CCR metadata entry is empty: snapshot wrote the handle but metadata persistence failed, the store was pruned between snapshot and act, or a handle was constructed/replayed rather than obtained from browser_snapshot.","commonSituations":"Agent reuses a recovery handle from a previous session after state cleanup; CCR store corrupted or reset under a fresh CAVEMAN_HOME; crash between snapshot payload write and metadata write.","solutions":["Call browser_snapshot again to mint a fresh recovery handle and its metadata, then act with the new uids.","If it persists, check the CCR store under CAVEMAN_HOME for truncation/permission problems (state writes must be atomic, mode 0600).","Never replay handles across sessions — they name compressed state that may have been reclaimed."],"exampleFix":"// before\nact(handle: \"h-169...\", target: \"uid-9\", action: \"click\")  // metadata empty\n\n// after\nres = browser_snapshot()\nact(handle: res.recovery_handle, target: \"<uid from res>\", action: \"click\")","handlingStrategy":"validation","validationCode":"// Go: verify recovery metadata is present before acting\nfunc (s *Session) targetsForHandle(handle string) (map[string]Target, error) {\n    meta, err := s.eng.RetrieveMetadata(handle)\n    if err != nil {\n        return nil, err\n    }\n    if len(meta) == 0 {\n        return nil, errors.New(\"empty a11y recovery metadata\") // re-snapshot upstream\n    }\n    // ...\n}","typeGuard":null,"tryCatchPattern":"// Go: on empty metadata, mint a new snapshot instead of failing the whole session\nif errors.Is(err, errEmptyRecoveryMeta) {\n    snap, _ := s.snapshot(ctx)\n    return s.act(ctx, snap.Handle, target, action)\n}","preventionTips":["Always act with the recovery handle returned by the most recent browser_snapshot call.","Never persist or replay handles across sessions — CCR state may be reclaimed.","Keep CAVEMAN_HOME state writes atomic (mode 0600) and avoid pruning the store mid-session."],"tags":["browser","a11y","recovery","state","mcp"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}