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

  1. Run bd list or bd show to confirm the exact issue ID.
  2. Check you are in the repo/database where the issue was created (sync may be needed: bd dolt pull).
  3. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/21802fbe04028473. Report an issue: GitHub.