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

  1. Verify the ID exists: `bd show <id>` or `bd list | grep <id>`.
  2. Re-list the molecule's children (`bd mol progress <molecule>`) to get valid current IDs.
  3. 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

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


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