{"record":{"id":"ddf20118eee0daac","repo":"gastownhall/beads","slug":"failed-to-get-wisp-labels-w","errorCode":null,"errorMessage":"failed to get wisp labels: %w","messagePattern":"failed to get wisp labels: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/wisps.go","lineNumber":160,"sourceCode":"\t\treturn configPrefix + \"-\" + issue.IDPrefix\n\t}\n\treturn configPrefix + \"-wisp\"\n}\n\n// getWisp retrieves an issue from the wisps table.\nfunc (s *DoltStore) getWisp(ctx context.Context, id string) (*types.Issue, error) {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\tissue, err := scanIssueFromTable(ctx, s.db, \"wisps\", id)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif issue == nil {\n\t\treturn nil, nil\n\t}\n\tlabels, err := s.getWispLabels(ctx, id)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get wisp labels: %w\", err)\n\t}\n\tissue.Labels = labels\n\treturn issue, nil\n}\n\n// getWispLabels retrieves labels from the wisp_labels table.\nfunc (s *DoltStore) getWispLabels(ctx context.Context, issueID string) ([]string, error) {\n\trows, err := s.queryContext(ctx, `SELECT label FROM wisp_labels WHERE issue_id = ? ORDER BY label`, issueID)\n\tif err != nil {\n\t\treturn nil, wrapQueryError(\"get wisp labels\", err)\n\t}\n\tdefer rows.Close()\n\n\tvar labels []string\n\tfor rows.Next() {\n\t\tvar label string\n\t\tif err := rows.Scan(&label); err != nil {\n\t\t\treturn nil, wrapScanError(\"scan wisp label\", err)","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/wisps.go#L142-L178","documentation":"getWisp successfully loaded the issue row but the follow-up s.getWispLabels(ctx, id) call failed; the label-read error is wrapped as 'failed to get wisp labels'. The whole lookup fails even though the issue itself was found, because labels are an integral part of the returned issue.","triggerScenarios":"Any error from the wisp_labels table read during getWisp: missing wisp_labels table, driver error, context cancellation, or connection failure while fetching labels for an existing wisp.","commonSituations":"Older database missing the wisp_labels table after a binary upgrade; transient Dolt/connection errors; context timeout while the label query runs; schema drift on wisp_labels.","solutions":["Read the wrapped cause to identify whether it is schema, connection, or context related","Verify the wisp_labels table exists and matches the expected schema (bd doctor / migrate)","Retry on transient connection errors; check DB connectivity","Increase the context timeout if the label query is timing out"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"var n int\nerr := db.QueryRow(`SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'wisp_labels'`).Scan(&n)\nif err == nil && n == 0 {\n    return fmt.Errorf(\"wisp_labels table missing; run migration/doctor before lookups\")\n}","typeGuard":null,"tryCatchPattern":"issue, err := store.GetIssue(ctx, id)\nif err != nil && strings.Contains(err.Error(), \"failed to get wisp labels\") {\n    // transient or schema issue on wisp_labels — retry after checking schema\n    return retryWithBackoff(func() error { _, err = store.GetIssue(ctx, id); return err })\n}","preventionTips":["Run bd doctor / migrations after upgrades so wisp_labels exists and matches schema","Use contexts with adequate timeouts for multi-query lookups","Monitor DB connectivity; label fetches add a second round trip per lookup","Wrap the wrapped cause: log %v of the inner error for driver specifics"],"tags":["database","labels","schema","go"],"backgroundTag":"label-lookup-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}