{"record":{"id":"a3591b89aad3c5bf","repo":"gastownhall/beads","slug":"w-issue-s","errorCode":null,"errorMessage":"%w: issue %s","messagePattern":"%w: issue (.+?)","errorType":"exception","errorClass":"storage.ErrNotFound","httpStatus":null,"severity":"warning","filePath":"internal/storage/dolt/wisps.go","lineNumber":37,"sourceCode":"\n// insertIssueIntoTable delegates to the shared issueops.InsertIssueIntoTable.\nfunc insertIssueIntoTable(ctx context.Context, tx *sql.Tx, table string, issue *types.Issue) error {\n\treturn issueops.InsertIssueIntoTable(ctx, tx, table, issue)\n}\n\n// scanIssueFromTable scans a single issue from the specified table.\n//\n//nolint:gosec // G201: table is a hardcoded constant (\"issues\" or \"wisps\")\nfunc scanIssueFromTable(ctx context.Context, db *sql.DB, table, id string) (*types.Issue, error) {\n\trow := db.QueryRowContext(ctx, fmt.Sprintf(`\n\t\tSELECT %s\n\t\tFROM %s %s\n\t\tWHERE id = ?\n\t`, issueSelectColumns, table, sqlbuild.LeaseJoin(table)), id)\n\n\tissue, err := scanIssueFrom(row)\n\tif err == sql.ErrNoRows {\n\t\treturn nil, fmt.Errorf(\"%w: issue %s\", storage.ErrNotFound, id)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get issue from %s: %w\", table, err)\n\t}\n\treturn issue, nil\n}\n\n// generateIssueIDInTable generates a unique ID, checking for collisions\n// in the specified table. Supports counter mode for non-ephemeral issues.\n//\n//nolint:gosec // G201: table is a hardcoded constant\nfunc generateIssueIDInTable(ctx context.Context, tx *sql.Tx, table, prefix string, issue *types.Issue, actor string) (string, error) {\n\t// Counter mode only applies to the issues table (not wisps).\n\tif table == \"issues\" {\n\t\tcounterMode, err := isCounterModeTx(ctx, tx)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/wisps.go#L19-L55","documentation":"This error is a wrapped storage.ErrNotFound returned when a wisp lookup by ID finds no row in the underlying table. scanIssueFromTable runs a SELECT ... WHERE id = ? and when sql.ErrNoRows comes back it wraps storage.ErrNotFound with the offending issue ID so callers can match with errors.Is. It is the library's canonical 'no such issue/wisp' signal, not a database failure.","triggerScenarios":"Calling getWisp (directly or via GetIssue/isActiveWisp) with an ID that does not exist in the wisps table; the ID was deleted, demoted/renamed, belongs to the wrong table, or was mistyped.","commonSituations":"A caller holds a stale ID after the wisp was closed and deleted; scripts referencing an issue from another repo/database; racing with demoteToWispInTx that moved the row; typos in hand-written IDs from CLI usage or automation.","solutions":["Verify the issue ID exists (e.g. bd show <id> or a SELECT on the wisps/issues table) and fix typos","Check errors.Is(err, storage.ErrNotFound) in the caller and handle it as an expected 'missing' path rather than a crash","Confirm you are pointed at the correct database (BEADS_DB / Dolt database) and table scope","If the item should exist, check whether it was closed/deleted or demoted and look up the new ID"],"exampleFix":"// before\nissue, err := store.GetIssue(ctx, id)\nif err != nil {\n    return err\n}\n// after\nissue, err := store.GetIssue(ctx, id)\nif err != nil {\n    if errors.Is(err, storage.ErrNotFound) {\n        return fmt.Errorf(\"issue %s does not exist\", id)\n    }\n    return err\n}","handlingStrategy":"type-guard","validationCode":"var exists bool\nerr := db.QueryRow(`SELECT COUNT(*) FROM wisps WHERE id = ?`, id).Scan(&exists)\nif err == nil && !exists {\n    return fmt.Errorf(\"issue %s not found\", id)\n}","typeGuard":"func IsNotFound(err error) bool {\n    return errors.Is(err, storage.ErrNotFound)\n}","tryCatchPattern":"issue, err := store.GetIssue(ctx, id)\nswitch {\ncase errors.Is(err, storage.ErrNotFound):\n    // handle missing record gracefully\n    return nil\ncase err != nil:\n    return fmt.Errorf(\"lookup %s: %w\", id, err)\n}","preventionTips":["Always match with errors.Is(err, storage.ErrNotFound), never string comparison","Validate IDs against a listing before mutating or deep-processing them","Refresh stale IDs after close/demote operations instead of caching them","Confirm database/scope configuration before lookups in scripts"],"tags":["not-found","database","go"],"backgroundTag":"record-not-found","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}