gastownhall/beads · error

cannot resolve issue ID %q: storage is nil

Error message

cannot resolve issue ID %q: storage is nil

What it means

ResolvePartialID resolves a partial or prefixed issue ID against storage. It immediately rejects a nil PartialIDResolverStore with this error, because no lookup is possible without a backing store.

Source

Thrown at internal/utils/id_parser.go:54

		return input
	}

	return prefix + input
}

// ResolvePartialID resolves a potentially partial issue ID to a full ID.
// Supports:
// - Full IDs: "bd-a3f8e9" or "a3f8e9" → "bd-a3f8e9"
// - Without hyphen: "bda3f8e9" or "wya3f8e9" → "bd-a3f8e9"
// - Partial IDs: "a3f8" → "bd-a3f8e9" (if unique match)
// - Hierarchical: "a3f8e9.1" → "bd-a3f8e9.1"
//
// Returns an error if:
// - No issue found matching the ID
// - Multiple issues match (ambiguous prefix)
func ResolvePartialID(ctx context.Context, store PartialIDResolverStore, input string) (string, error) {
	if store == nil {
		return "", fmt.Errorf("cannot resolve issue ID %q: storage is nil", input)
	}

	// Fast path: Use SearchIssues with exact ID filter (GH#942).
	// This uses the same query path as "bd list --id", ensuring consistency.
	// Previously we used GetIssue which could fail in cases where SearchIssues
	// with filter.IDs succeeded, likely due to subtle query differences.
	exactFilter := types.IssueFilter{IDs: []string{input}}
	if issues, err := store.SearchIssues(ctx, "", exactFilter); err == nil && len(issues) > 0 {
		return issues[0].ID, nil
	}

	// Get the configured prefix
	prefix, err := store.GetConfig(ctx, "issue_prefix")
	if err != nil || prefix == "" {
		prefix = "bd"
	}

	// Ensure prefix has hyphen for ID format

View on GitHub (pinned to 71377f2769)

Solutions

  1. Initialize/open the storage backend and pass the resulting store to ResolvePartialID
  2. Fix DI wiring so the PartialIDResolverStore is set before any ID resolution runs
  3. Guard call sites: skip resolution when the store is nil, or return a clearer init error

Example fix

// before
id, err := utils.ResolvePartialID(ctx, nil, "a3f8") // storage is nil
// after
store, err := storage.Open(ctx, dbPath)
if err != nil { return err }
id, err := utils.ResolvePartialID(ctx, store, "a3f8")
Defensive patterns

Strategy: type-guard

Validate before calling

func resolveSafely(ctx context.Context, store utils.PartialIDResolverStore, input string) (string, error) {
	if store == nil {
		return "", fmt.Errorf("storage not initialized: open the database before resolving IDs")
	}
	return utils.ResolvePartialID(ctx, store, input)
}

Type guard

func storeReady(store utils.PartialIDResolverStore) bool { return store != nil }

Try / catch

id, err := utils.ResolvePartialID(ctx, store, input)
if err != nil {
	if strings.Contains(err.Error(), "storage is nil") {
		return fmt.Errorf("run 'bd init' or open the database first: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling ResolvePartialID(ctx, nil, input) — directly or via ResolvePartialID(s) — before a store/database handle has been opened or assigned.

Common situations: Calling ID resolution during CLI startup before the Dolt/SQLite store is initialized; dependency-injection wiring that left the store field nil; tests constructing the resolver without a store.

Related errors


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