gastownhall/beads · error

no slot %q on %s: no metadata

Error message

no slot %q on %s: no metadata

What it means

SlotGet found the issue but its metadata column is empty (zero-length JSON), so no slot key can exist on it. This is a deliberately descriptive 'not found' error telling you the issue has no metadata map at all, as opposed to having metadata without the requested key.

Source

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

// 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:
		return v, nil
	default:
		// Non-string values are returned as JSON
		raw, err := json.Marshal(v)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Call SlotSet/MergeMetadata to create the slot before reading it.
  2. Treat this as a miss and provide a default value in the caller instead of erroring.
  3. Check with bd or the metadata column whether the issue was expected to carry metadata.

Example fix

// before
val, err := store.SlotGet(ctx, id, "owner")
if err != nil {
	return err
}
// after
val, err := store.SlotGet(ctx, id, "owner")
if err != nil && strings.Contains(err.Error(), "no slot") {
	val = "unassigned" // default when the issue has no metadata
}
Defensive patterns

Strategy: fallback

Validate before calling

issue, err := store.GetIssue(ctx, issueID)
if err == nil && len(issue.Metadata) == 0 {
	// no metadata; skip SlotGet and use a default
}

Try / catch

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

Prevention

When it happens

Trigger: Calling SlotGet on an issue that was never given any metadata (no SlotSet/MergeMetadata ever called on it).

Common situations: Reading a slot before ever setting it; querying an old issue created before the slot feature was used; assuming SlotSet ran when it actually failed earlier.

Related errors


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