gastownhall/beads · error
issue %s not found
Error message
issue %s not found
What it means
Returned when workapi.GetIssueOrWisp reports storage.ErrNotFound for the given issue ID during a proxied heartbeat command. It means no issue (classic or wisp) with that ID exists in the database. This is an expected, user-facing 'unknown ID' error, not an internal failure.
Source
Thrown at cmd/bd/heartbeat_proxied_server.go:37
// runHeartbeatProxiedServer routes bd heartbeat through the proxied-server
// plane. The lease write is EPHEMERAL (the dolt_ignored leases table,
// bd-lrgn1): the transaction commits via uow.RunTxEphemeral's SQL-only form,
// so a heartbeat mints exactly ZERO Dolt commits per invocation — the same
// commit discipline as the classic DoltStore.HeartbeatIssue, and deliberately
// nothing here sets commandDidWrite or creates a Dolt commit. The exit code
// is the worker contract (workers call this every ~90s and only check rc):
// 0 = lease refreshed; nonzero = the lease is gone (wrong owner, not
// in_progress, closed, reclaimed) and the worker should stop.
func runHeartbeatProxiedServer(ctx context.Context, id string) error {
if uowProvider == nil {
return HandleErrorRespectJSON("proxied-server UOW provider not initialized")
}
res, err := uow.RunTxEphemeral(ctx, uowProvider, func(ctx context.Context, uw uow.UnitOfWork) (heartbeatProxiedOutcome, error) {
issue, _, rerr := workapi.GetIssueOrWisp(ctx, workapi.NewUOWDetailSource(uw), id)
if errors.Is(rerr, storage.ErrNotFound) {
return heartbeatProxiedOutcome{}, fmt.Errorf("issue %s not found", id)
}
if rerr != nil {
return heartbeatProxiedOutcome{}, fmt.Errorf("resolving %s: %w", id, rerr)
}
// Wisps resolve here too and are refused below: the repo verb
// classifies them ErrNotClaimable ("is ephemeral"), same as classic.
if herr := uw.IssueUseCase().Heartbeat(ctx, issue.ID, actor); herr != nil {
return heartbeatProxiedOutcome{}, fmt.Errorf("heartbeat %s: %w", issue.ID, herr)
}
return heartbeatProxiedOutcome{id: issue.ID, title: issue.Title}, nil
})
if err != nil {
return HandleErrorRespectJSON("%v", err)
}
SetLastTouchedID(res.id)
return renderHeartbeatSuccess(res.id, res.title)
}View on GitHub (pinned to 71377f2769)
Solutions
- Run bd list or bd show to confirm the exact issue ID.
- Check you are in the repo/database where the issue was created (sync may be needed: bd dolt pull).
- If the issue was deleted, create a new issue instead of heartbeating the stale ID.
Example fix
// before bd heartbeat bd-9999 // error: issue bd-9999 not found // after: resolve the real ID first bd list --json | jq '.[].id' bd heartbeat bd-123
Defensive patterns
Strategy: validation
Validate before calling
// before heartbeating, confirm the ID exists bd list --json | jq -e --arg id "$ID" 'any(.[]; .id == $id)'
Try / catch
if strings.Contains(err.Error(), "not found") {
// prompt user for correct ID via bd list / bd show
} Prevention
- Copy issue IDs from bd list output rather than typing them.
- Sync (bd dolt pull) before operating on issues created elsewhere.
- Script against full IDs, not ambiguous prefixes.
When it happens
Trigger: bd heartbeat <id> (proxied server path) is run with an ID that GetIssueOrWisp cannot resolve; errors.Is(rerr, storage.ErrNotFound) is true and the message is formatted with the requested ID.
Common situations: Typo in the issue ID, issue was deleted or closed and cleaned up, heartbeat issued against a different database/repo than where the issue lives, or prefix abbreviation that matches nothing.
Related errors
- resolving %s: %w
- resolving parent %q: not found
- no store is open for this workspace
- resolving ID %s: no issue found matching %q
- proxied-server provider %T does not offer the batch-close su
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/21802fbe04028473.
Report an issue: GitHub.