gastownhall/beads · error

no slot %q on %s: key not found

Error message

no slot %q on %s: key not found

What it means

The issue has valid metadata JSON, but the requested slot key is absent from that JSON object. SlotGet uses the comma-ok map lookup and returns this descriptive error instead of an empty string, distinguishing 'metadata exists, key missing' from the no-metadata case.

Source

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

// 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:
		return v, nil
	default:
		// Non-string values are returned as JSON
		raw, err := json.Marshal(v)
		if err != nil {
			return "", fmt.Errorf("marshaling slot value for %s.%s: %w", issueID, key, err)
		}
		return string(raw), nil
	}
}

// SlotClear removes a metadata key from an issue.
// It is not an error to clear a key that doesn't exist.
//

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the key spelling and exact case against what SlotSet wrote.
  2. Set the slot first (SlotSet) or fall back to a default value in the caller.
  3. List the issue's metadata (query the metadata column) to see which keys exist.

Example fix

// before
val, err := store.SlotGet(ctx, id, "Owner") // key was set as "owner"
// after
val, err := store.SlotGet(ctx, id, "owner") // exact key as stored
Defensive patterns

Strategy: validation

Validate before calling

issue, _ := store.GetIssue(ctx, issueID)
var m map[string]interface{}
if json.Unmarshal(issue.Metadata, &m) == nil {
	if _, ok := m[key]; !ok {
		// key absent; set it or use a default before SlotGet
	}
}

Type guard

func slotExists(issue *storage.Issue, key string) bool {
	var m map[string]interface{}
	if json.Unmarshal(issue.Metadata, &m) != nil {
		return false
	}
	_, ok := m[key]
	return ok
}

Try / catch

val, err := store.SlotGet(ctx, id, key)
if err != nil && strings.Contains(err.Error(), "key not found") {
	val = defaultValue
}

Prevention

When it happens

Trigger: Calling SlotGet with a key that was never set via SlotSet/MergeMetadata, or one that was already removed by SlotClear.

Common situations: Typo in the slot key (case-sensitive lookup); SlotClear ran earlier; reading an optional slot on issues where only some have it set.

Related errors


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