{"record":{"id":"21ecce5dd8f0b7c0","repo":"gastownhall/beads","slug":"descendants-wisps-table-probe-w","errorCode":null,"errorMessage":"descendants: wisps table probe: %w","messagePattern":"descendants: wisps table probe: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue_descendants.go","lineNumber":57,"sourceCode":"\tlevelFilter := filter\n\tlevelFilter.ParentID = nil\n\tlevelFilter.Limit = 0\n\tlevelFilter.Offset = 0\n\n\tissueWhereClauses, issueArgs, err := buildIssueFilterClauses(\"\", levelFilter, issuesFilterTables)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"descendants: issues filter: %w\", err)\n\t}\n\n\twispDepsExist, err := r.optionalTableExists(ctx, \"wisp_dependencies\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"descendants: wisp_dependencies probe: %w\", err)\n\t}\n\twalkWisps := wispDepsExist && !filter.SkipWisps\n\tif walkWisps {\n\t\tempty, probeErr := r.wispsTableEmptyOrMissing(ctx)\n\t\tif probeErr != nil {\n\t\t\treturn nil, fmt.Errorf(\"descendants: wisps table probe: %w\", probeErr)\n\t\t}\n\t\twalkWisps = !empty\n\t}\n\n\tvar wispWhereClauses []string\n\tvar wispArgs []any\n\tif walkWisps {\n\t\twispWhereClauses, wispArgs, err = buildIssueFilterClauses(\"\", levelFilter, wispsFilterTables)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"descendants: wisps filter: %w\", err)\n\t\t}\n\t}\n\n\tissuePred := buildDescendantsPred(\"issues\", \"i\", \"issue_matches\", issueWhereClauses, issueArgs)\n\tvar wispPred predBundle\n\tif walkWisps {\n\t\twispPred = buildDescendantsPred(\"wisps\", \"w\", \"wisp_matches\", wispWhereClauses, wispArgs)\n\t}","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue_descendants.go#L39-L75","documentation":"GetDescendants wraps a failure from wispsTableEmptyOrMissing, the probe that decides whether the recursive CTE should include a wisps branch. The probe runs `SELECT 1 FROM wisps LIMIT 1`; any error that is neither sql.ErrNoRows nor a table-not-exist error (e.g. connection loss, engine failure, permissions) is wrapped as \"descendants: wisps table probe\". It signals the store could not even determine whether the ephemeral wisp plane exists, not a data problem.","triggerScenarios":"Calling GetDescendants (or bd's hierarchical --parent walk) when the underlying `SELECT 1 FROM wisps LIMIT 1` probe fails with a non-missing-table error: the Dolt server dropped mid-query, the connection was closed, the database file is corrupted/locked, or the user lacks SELECT on the wisps table.","commonSituations":"Dolt server restarted or OOM-killed during a bd command; stale connection pool after a network blip; a partially-migrated database where wisps exists but is unreadable; running bd against a replica or read-only mount with restricted grants.","solutions":["Check Dolt/database connectivity (bd doctor, server logs) — the probe failure is a storage-layer symptom, not a filter problem","Retry the command; transient connection drops between the wisp_dependencies probe and this probe are the usual cause","Verify the wisps table is readable: run the same SELECT manually as the bd user and check grants","If the database is corrupted or locked, restore from backup or run repairs before using GetDescendants"],"exampleFix":"// before: probing per call on a possibly-dead connection\npage, err := repo.GetDescendants(ctx, rootID, filter)\n// after: validate the store is reachable first\nif err := store.Ping(ctx); err != nil {\n    return fmt.Errorf(\"store unreachable: %w\", err)\n}\npage, err := repo.GetDescendants(ctx, rootID, filter)","handlingStrategy":"retry","validationCode":"if err := store.Ping(ctx); err != nil {\n    return fmt.Errorf(\"store unreachable, aborting descendants walk: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"issues, err := repo.GetDescendants(ctx, rootID, filter)\nif err != nil && strings.Contains(err.Error(), \"wisps table probe\") {\n    // transient storage failure: retry with backoff\n    return retryWithBackoff(3, func() error { _, err := repo.GetDescendants(ctx, rootID, filter); return err })\n}","preventionTips":["Health-check the store (Ping / bd doctor) before long hierarchy walks","Use context timeouts generous enough for deep walks","Keep Dolt server monitored for restarts and OOM kills","Grant the bd user SELECT on all tables including wisps"],"tags":["database","dolt","storage","descendants","probe"],"backgroundTag":"database-connection-lost","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}