gastownhall/beads · error

getting issue %s: %w

Error message

getting issue %s: %w

What it means

SlotGet failed while fetching the underlying issue before reading its metadata slot. The original error from GetIssue (issue not found, DB connection failure, query error, etc.) is wrapped with the issue ID so the caller knows which record failed. This is a wrapper error: the root cause is the embedded %w error.

Source

Thrown at internal/storage/dolt/slots.go:93

//
// Built on MergeMetadata so the read-modify-write is atomic: two concurrent
// SlotSet calls on different keys both survive. The string value is stored as a
// JSON string (json.Marshal(value) yields "value"), keeping the stored metadata
// byte-compatible with the historical whole-metadata rewrite.
func (s *DoltStore) SlotSet(ctx context.Context, issueID, key, value, actor string) error {
	raw, err := json.Marshal(value)
	if err != nil {
		return fmt.Errorf("marshaling slot value for %s.%s: %w", issueID, key, err)
	}
	return s.MergeMetadata(ctx, issueID, key, raw, actor)
}

// SlotGet retrieves the value of a metadata key from an issue.
// Returns an error if the issue has no metadata or the key is not found.
func (s *DoltStore) SlotGet(ctx context.Context, issueID, key string) (string, error) {
	issue, err := s.GetIssue(ctx, issueID)
	if err != nil {
		return "", fmt.Errorf("getting issue %s: %w", issueID, err)
	}

	if len(issue.Metadata) == 0 {
		return "", fmt.Errorf("no slot %q on %s: no metadata", key, issueID)
	}

	metadata := make(map[string]interface{})
	if err := json.Unmarshal(issue.Metadata, &metadata); err != nil {
		return "", fmt.Errorf("parsing metadata for %s: %w", issueID, err)
	}

	val, ok := metadata[key]
	if !ok {
		return "", fmt.Errorf("no slot %q on %s: key not found", key, issueID)
	}

	switch v := val.(type) {
	case string:

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the issue ID exists (e.g. bd show <id> or query the issues table) before calling SlotGet.
  2. Inspect the wrapped cause via errors.Unwrap / errors.Is to distinguish 'not found' from connection failures.
  3. Confirm the Dolt database is reachable and the correct branch is checked out (bd doctor).

Example fix

// before
val, err := store.SlotGet(ctx, id, key)
if err != nil {
	return err // ambiguous root cause
}
// after
val, err := store.SlotGet(ctx, id, key)
if err != nil {
	if errors.Is(err, storage.ErrNotFound) {
		return fmt.Errorf("issue %s not found", id)
	}
	return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := store.GetIssue(ctx, issueID); err != nil {
	// issue missing or DB down; skip SlotGet
	return err
}

Type guard

func isNotFound(err error) bool {
	return errors.Is(err, storage.ErrNotFound)
}

Try / catch

val, err := store.SlotGet(ctx, id, key)
if err != nil {
	var inner error
	if errors.As(err, &inner) && errors.Is(inner, storage.ErrNotFound) {
		// handle missing issue
	}
	return err
}

Prevention

When it happens

Trigger: Calling DoltStore.SlotGet(ctx, issueID, key) when GetIssue fails — most commonly the issue ID does not exist, the database is unreachable, or the underlying SQL query fails.

Common situations: Typo'd or stale issue ID passed to SlotGet; issue was deleted between ID capture and the slot read; embedded Dolt server not running; wrong --db path so the issues table is empty.

Related errors


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