gastownhall/beads · error
db: LabelSQLRepository.Insert %s/%s: issue does not exist
Error message
db: LabelSQLRepository.Insert %s/%s: issue does not exist
What it means
Returned when INSERT IGNORE affected 0 rows and the verification query confirms the parent issue does not exist in the issues (or wisps) table. The library treats this as a caller error: you are adding a label to a nonexistent issue, so nothing is inserted and no event is journaled.
Source
Thrown at internal/storage/domain/db/label.go:73
if err != nil {
return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: %w", issueID, label, err)
}
rows, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: rows affected: %w", issueID, label, err)
}
if rows == 0 {
issueTable := "issues"
if opts.UseWispsTable {
issueTable = "wisps"
}
var count int
//nolint:gosec // G201: issueTable is one of two hardcoded constants.
if err := r.runner.QueryRowContext(ctx, fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE id = ?", issueTable), issueID).Scan(&count); err != nil {
return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: verify issue: %w", issueID, label, err)
}
if count == 0 {
return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: issue does not exist", issueID, label)
}
return nil
}
if err := r.events.Record(ctx, domain.Event{
IssueID: issueID,
Type: types.EventLabelAdded,
Actor: actor,
NewValue: label,
}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {
return err
}
// A label is part of the bead snapshot; the idempotent no-op path above
// returns without writing and journals nothing.
return issueops.RecordEventInTx(ctx, r.runner, issueops.EventUpdate, issueID, actor)
}
func (r *labelSQLRepositoryImpl) Delete(ctx context.Context, issueID, label, actor string, opts domain.LabelOpts) error {
if issueID == "" {View on GitHub (pinned to 71377f2769)
Solutions
- Create the issue first, then insert the label
- Verify the issue ID exists (e.g. `bd show <id>`) before labeling
- Check UseWispsTable matches the table the issue actually resides in
- Use the full canonical issue ID, not a prefix
Example fix
// before
repo.Insert(ctx, "bd-abc", "bug", opts) // ID may not exist
// after
if _, err := issueRepo.Get(ctx, "bd-abc-1234"); err != nil { return err }
return repo.Insert(ctx, "bd-abc-1234", "bug", opts) Defensive patterns
Strategy: validation
Validate before calling
func issueExists(ctx context.Context, db *sql.DB, id string, wisps bool) (bool, error) {
t := "issues"; if wisps { t = "wisps" }
var n int
if err := db.QueryRowContext(ctx, "SELECT COUNT(*) FROM "+t+" WHERE id = ?", id).Scan(&n); err != nil { return false, err }
return n > 0, nil
} Try / catch
if err := repo.Insert(ctx, issueID, label, opts); err != nil {
if strings.Contains(err.Error(), "issue does not exist") {
return fmt.Errorf("cannot label %s: create the issue first", issueID)
}
return err
} Prevention
- Always create the issue before adding labels
- Use full canonical issue IDs from the API, never prefixes
- Refresh stale local caches of issue IDs
- Set UseWispsTable correctly for wisps vs issues
When it happens
Trigger: Calling Insert with an issueID that was never created, was deleted, or an ID format mismatch (e.g. using a short prefix instead of the full ID); Insert into wisps mode when the issue lives in `issues` instead.
Common situations: Stale client cache holding a deleted issue ID; passing a wisp ID while UseWispsTable is false; race where the issue was removed between read and label insert; typos in hand-built IDs.
Related errors
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/5347307239be62a7.
Report an issue: GitHub.