gastownhall/beads · error

invalid storage class %q (must be %s)

Error message

invalid storage class %q (must be %s)

What it means

ParseStorageClass validates a user-supplied storage-class value from a flag or config. Empty and unrecognized values are rejected, and the error lists the valid names via ValidStorageClassNames() so the user can self-correct. Callers that allow 'unset' must check for empty before calling.

Source

Thrown at internal/types/types.go:1032

func (s StorageClass) IsValid() bool {
	if s == "" {
		return true
	}
	return slices.Contains(validStorageClasses, s)
}

// ValidStorageClassNames enumerates the accepted values for error messages.
func ValidStorageClassNames() string {
	return joinNamesWithOr(validStorageClasses)
}

// ParseStorageClass validates a user-supplied storage-class value (flag or
// config). Empty is rejected here — callers that allow "unset" check for
// empty before parsing.
func ParseStorageClass(v string) (StorageClass, error) {
	s := StorageClass(v)
	if s == "" || !s.IsValid() {
		return "", fmt.Errorf("invalid storage class %q (must be %s)", v, ValidStorageClassNames())
	}
	return s, nil
}

// Normalize maps the explicit versioned spelling to unset: the two are
// semantically identical and the marker is omitted when versioned in storage
// cells and serialized records alike. Both insert stacks call this so
// the database never persists the literal "versioned".
func (s StorageClass) Normalize() StorageClass {
	if s == StorageClassVersioned {
		return ""
	}
	return s
}

// EffectiveStorageClass resolves the record's class: an explicit declaration
// wins; otherwise wisp-plane records (Ephemeral or NoHistory) are ephemeral
// and everything else is versioned.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Use one of the names printed in the error message (see ValidStorageClassNames())
  2. For optional values, skip parsing when the input is empty — ParseStorageClass rejects empty by design
  3. Route all user input through ParseStorageClass and fix at the config/flag source, never cast raw strings to StorageClass

Example fix

// before
sc, err := ParseStorageClass("ephemaral") // typo
// after
sc, err := ParseStorageClass("ephemeral")
Defensive patterns

Strategy: validation

Validate before calling

if v == "" {
    return nil // unset is allowed by this caller
}
if _, err := ParseStorageClass(v); err != nil {
    return fmt.Errorf("--storage-class: %v", err)
}

Type guard

func isKnownStorageClass(s string) bool {
    return StorageClass(s).IsValid()
}

Try / catch

sc, err := ParseStorageClass(v)
if err != nil {
    return fmt.Errorf("bad --storage-class %q: %w", v, err) // error already lists valid values
}

Prevention

When it happens

Trigger: Calling ParseStorageClass("") or with any string not in validStorageClasses — e.g. --storage-class=ephemaral, 'ssd', or a value from an old config schema.

Common situations: Typos on the command line; copying flag values from another tool with different class names; stale config written before class names were renamed.

Related errors


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