{"record":{"id":"dd09f1ddb9c0cbfb","repo":"gastownhall/beads","slug":"descendants-hydrate-issues-w","errorCode":null,"errorMessage":"descendants: hydrate issues: %w","messagePattern":"descendants: hydrate issues: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue_descendants.go","lineNumber":90,"sourceCode":"\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)\n\t}\n\n\tissuesByID, err := r.fetchIssuesByIDs(ctx, page.issueIDs, issuesFilterTables, filter)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"descendants: hydrate issues: %w\", err)\n\t}\n\n\tvar wispsByID map[string]*types.Issue\n\tif len(page.wispIDs) > 0 {\n\t\twispsByID, err = r.fetchIssuesByIDs(ctx, page.wispIDs, wispsFilterTables, filter)\n\t\tif err != nil && !dberrors.IsTableNotExist(err) {\n\t\t\treturn nil, fmt.Errorf(\"descendants: hydrate wisps: %w\", err)\n\t\t}\n\t}\n\n\treturn reassembleBySrc(page.ordered, issuesByID, wispsByID), nil\n}\n\n// buildDescendantsCTE walks parent-child edges AND the dotted-ID fallback the\n// classic ParentID filter applies (issueops/filters.go): a row named\n// <node>.<suffix> with no parent-child edge at all is a child of <node>.\n// Rows carry a via marker: 'e' for edge-found, 'd' for dotted-found. Dotted\n// recursion only fires from 'e' rows — a dotted node's own dotted","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue_descendants.go#L72-L108","documentation":"GetDescendants wraps a failure from fetchIssuesByIDs when hydrating the durable issue rows matched by the descendants walk. After the CTE returns IDs, each ID is loaded from the issues table (with optional label/dependency hydration); any SQL or hydration error there is wrapped as \"descendants: hydrate issues\". Unlike the wisps hydration, table-not-exist is NOT tolerated here because issues is a mandatory table.","triggerScenarios":"Calling GetDescendants when the follow-up `SELECT ... FROM issues WHERE id IN (...)` or its hydrateIssues step (labels/dependencies) fails: missing issues/labels/dependencies tables, connection drop, scan error, or ctx cancellation.","commonSituations":"Corrupted or partially migrated database missing the labels or dependencies tables; too many descendant IDs producing an oversized IN clause / packet; connection pool exhausted during hydration.","solutions":["Read the wrapped inner error to see whether the base fetch or the label/dependency hydration failed","Run bd doctor to check mandatory tables (issues, labels, dependencies) exist and are consistent","Retry on transient connection errors; shrink the result set with filter fields or MaxRows if the batch is huge","Restore the database from backup if a required table is missing"],"exampleFix":"// before: assuming any hydration failure is transient\nissues, err := repo.GetDescendants(ctx, root, filter)\n// after: distinguish and diagnose\nissues, err := repo.GetDescendants(ctx, root, filter)\nif err != nil && strings.Contains(err.Error(), \"hydrate issues\") {\n    log.Printf(\"run bd doctor: mandatory table problem: %v\", err)\n}","handlingStrategy":"validation","validationCode":"if err := bdDoctor(ctx); err != nil {\n    return fmt.Errorf(\"mandatory tables broken, fix before GetDescendants: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"issues, err := repo.GetDescendants(ctx, rootID, filter)\nif err != nil && strings.Contains(err.Error(), \"hydrate issues\") {\n    if dberrors.IsTableNotExist(errors.Unwrap(err)) {\n        // schema is missing a mandatory table: restore/repair, do not retry\n    }\n}","preventionTips":["Run bd doctor after migrations, clones, and version upgrades","Never drop or rename issues/labels/dependencies manually","Back up the database before schema changes","Retry only transient errors; table-not-exist needs repair, not retry"],"tags":["database","hydration","issues","labels"],"backgroundTag":"missing-table","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}