gastownhall/beads · error
issue %s not found
Error message
issue %s not found
What it means
`uowMolReader.GetIssue` resolves an ID as either an issue or a wisp via `workapi.GetIssueOrWisp`. When the resolver reports `storage.ErrNotFound`, the port translates it into a plain `issue %s not found` error. This is the port's canonical 'this ID does not exist' signal for all molecule read commands.
Source
Thrown at cmd/bd/mol_port.go:127
if current.Status != types.StatusOpen {
return fmt.Errorf("step %s already claimed (status: %s)", id, current.Status)
}
return tx.UpdateIssue(ctx, id, map[string]interface{}{"status": types.StatusInProgress}, actor)
})
}
func newStandaloneStoreMolWriter(store storage.DoltStorage) storeMolWriter {
return storeMolWriter{DoltStorage: store}
}
type uowMolReader struct {
uw uow.UnitOfWork
}
func (r uowMolReader) GetIssue(ctx context.Context, id string) (*types.Issue, error) {
issue, isWisp, rerr := workapi.GetIssueOrWisp(ctx, workapi.NewUOWDetailSource(r.uw), id)
if errors.Is(rerr, storage.ErrNotFound) {
return nil, fmt.Errorf("issue %s not found", id)
}
if rerr != nil {
return nil, fmt.Errorf("resolving %s: %w", id, rerr)
}
// READS, ALL OF THEM, and they stay for the reason the writes did not.
// uowMolReader is a PORT: it adapts a caller's open unit of work to the
// molecule commands' reader interface, and every method here must answer
// from inside that transaction. issueops.Reader opens one of its own, so a
// role-routed port would show the molecule the last committed state while
// the command that owns the transaction is midway through changing it.
// A reader role bound to a caller's transaction is the follow-up
// (ga-2ltro.12). The wisp branch here is a read that follows the row
// GetIssueOrWisp already found, not a front door choosing where to write.
var labels []string
var err error
if isWisp {
labels, err = r.uw.LabelUseCase().GetWispLabels(ctx, id) //nolint:forbidigo // in-transaction port read; issueops.Reader would open its own
} else {View on GitHub (pinned to 71377f2769)
Solutions
- Verify the ID exists: `bd show <id>` or `bd list | grep <id>`.
- Re-list the molecule's children (`bd mol progress <molecule>`) to get valid current IDs.
- Sync first (`bd dolt pull`) if the issue may exist on another machine but not locally.
Example fix
// before stats, err := reader.GetMoleculeProgress(ctx, "bd-mol-42") // wrong id // after stats, err := reader.GetMoleculeProgress(ctx, "bd-100") // verified molecule id
Defensive patterns
Strategy: type-guard
Validate before calling
// shell: verify the ID resolves before molecule reads
bd show "$ID" --json >/dev/null 2>&1 || { echo "$ID not found locally; run bd dolt pull?"; exit 1; } Type guard
func issueExists(ctx context.Context, r MolReader, id string) bool {
iss, err := r.GetIssue(ctx, id)
return err == nil && iss != nil
} Try / catch
if iss, err := reader.GetIssue(ctx, id); err != nil {
if strings.Contains(err.Error(), "not found") {
return nil // expected absence; handle idempotently
}
return err
} Prevention
- Sync (`bd dolt pull`) before reading IDs produced on other machines.
- Copy IDs from command output instead of retyping them.
- Remember wisps may be flushed/compacted — don't cache wisp IDs long-term.
When it happens
Trigger: `GetMoleculeProgress`, `GetMoleculeLastActivity`, `runMolBurnProxiedServer`, or `runWispCreateProxiedServer` pass an ID to `GetIssue` that matches neither an issue nor a wisp — `errors.Is(rerr, storage.ErrNotFound)` fires and the formatted error is returned.
Common situations: Typo'd molecule/issue IDs; IDs belonging to a different database or before a sync; wisps that were flushed/compacted away; scripts referencing issues deleted after a `bd mol burn`.
Related errors
- step %s not found
- resolving ID %s: no issue found matching %q
- resolving --deps target %q: %w
- parent issue %s not found
- resolving issue ID %q: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/71190eebcc3bb73d.
Report an issue: GitHub.