{"record":{"id":"90dd658b80ecf07d","repo":"gastownhall/beads","slug":"descendants-wisps-filter-w","errorCode":null,"errorMessage":"descendants: wisps filter: %w","messagePattern":"descendants: wisps filter: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue_descendants.go","lineNumber":67,"sourceCode":"\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}\n\n\tcte, allArgs := buildDescendantsCTE(rootID, walkWisps, issuePred, wispPred)\n\n\trows, err := r.runner.QueryContext(ctx, cte, allArgs...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"descendants: query: %w\", err)\n\t}\n\tpage, err := scanIDSrcPage(rows)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"descendants: %w\", err)","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue_descendants.go#L49-L85","documentation":"GetDescendants wraps a failure from buildIssueFilterClauses when building the WHERE clauses for the wisps branch of the recursive descendants CTE. The same filter was already built successfully against the issues table, so a failure here almost always means a filter clause is invalid specifically for the wisps table layout (e.g. a column exists in one table config but not the other).","triggerScenarios":"Calling GetDescendants with an IssueFilter whose clauses reference wisp-incompatible fields, when the wisps table exists and is non-empty and SkipWisps is false — i.e. buildIssueFilterClauses(\"\", levelFilter, wispsFilterTables) returns an error.","commonSituations":"Using a filter field supported on durable issues but not on the wisps plane after a schema/config divergence; version mismatch where an older database lacks a column the filter builder expects in wispsFilterTables.","solutions":["Read the wrapped inner error to identify which filter clause failed to build","Remove or simplify the offending filter field and re-run GetDescendants","Check that the database schema matches the bd version (run bd doctor / migrations)","Set SkipWisps=true in the filter if you only need durable issues and the wisps plane is problematic"],"exampleFix":"// before\nissues, err := repo.GetDescendants(ctx, root, types.IssueFilter{Assignee: &a, Ephemeral: &t})\n// after: drop fields unsupported on the wisps plane, or scope to issues only\nissues, err := repo.GetDescendants(ctx, root, types.IssueFilter{Assignee: &a, SkipWisps: true})","handlingStrategy":"validation","validationCode":"// build the filter clauses yourself first; if this fails, the filter is bad\nif _, _, err := sqlbuild.BuildIssueFilterClauses(\"\", filter, sqlbuild.WispsFilterTables); err != nil {\n    return fmt.Errorf(\"filter unsupported on wisps plane: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"issues, err := repo.GetDescendants(ctx, rootID, filter)\nif err != nil && strings.Contains(err.Error(), \"wisps filter\") {\n    // fall back to durable issues only\n    filter.SkipWisps = true\n    issues, err = repo.GetDescendants(ctx, rootID, filter)\n}","preventionTips":["Only use filter fields documented as valid for both planes","Test new filter combinations against a workspace containing wisps","Pin database schema version to the bd binary version","Prefer SkipWisps=true when wisps data is irrelevant to the query"],"tags":["database","filter","query-builder","wisps"],"backgroundTag":"invalid-filter-clause","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}