headroomlabs-ai/headroom · error · ValueError

{token!r} not one of {list(field.choices)}

Error message

{token!r} not one of {list(field.choices)}

What it means

ValueError from _coerce in headroom/settings_store.py when an 'enum' field receives a value whose string form is not in the field's declared choices (line 794). The message lists the accepted choices. Note the value is coerced with str(), so numbers are compared by their string form. Reported per-field via SettingsValidationError.field_errors.

Source

Thrown at headroom/settings_store.py:794

            raise ValueError(f"expected a number, got {value!r}")
        number: int | float
        if field.type == "int":
            if isinstance(value, float) and not value.is_integer():
                raise ValueError(f"expected an integer, got {value!r}")
            number = int(value)
        else:
            number = float(value)
            if not math.isfinite(number):
                raise ValueError(f"expected a finite number, got {value!r}")
        if field.minimum is not None and number < field.minimum:
            raise ValueError(f"must be >= {field.minimum}")
        if field.maximum is not None and number > field.maximum:
            raise ValueError(f"must be <= {field.maximum}")
        return number
    if field.type == "enum":
        token = str(value)
        if token not in field.choices:
            raise ValueError(f"{token!r} not one of {list(field.choices)}")
        return token
    if field.type == "csv-list":
        tokens = value if isinstance(value, list | tuple) else str(value).split(",")
        tokens = [str(token).strip() for token in tokens]
        tokens = [token for token in tokens if token]
        return ",".join(tokens) if tokens else None
    if field.type == "header-map":
        if isinstance(value, dict):
            parsed = value
        else:
            try:
                parsed = json.loads(str(value))
            except (ValueError, TypeError) as exc:
                raise ValueError("expected a JSON object of header name/value strings") from exc
        if not isinstance(parsed, dict) or not all(
            isinstance(k, str) and isinstance(v, str) for k, v in parsed.items()
        ):
            raise ValueError("expected a JSON object of header name/value strings")

View on GitHub (pinned to 322425c43b)

Solutions

  1. Use one of the exact choices listed in the error message (match case exactly).
  2. If the value comes from user input, present the choices from the field metadata instead of a free-text box.
  3. After upgrading headroom, re-check enum choices for renamed values.

Example fix

# before
save({'mode': 'Cache'})  # ValueError: 'Cache' not one of ['cache', 'token']

# after
save({'mode': 'cache'})
Defensive patterns

Strategy: validation

Validate before calling

from headroom.settings_store import _BY_KEY

def valid_choice(key: str, v) -> bool:
    f = _BY_KEY.get(key)
    return f is None or str(v) in f.choices

Type guard

def is_valid_enum(key: str, v: str) -> bool:
    f = _BY_KEY.get(key)
    return f is not None and str(v) in f.choices

Try / catch

except SettingsValidationError as e:
    for key, msg in e.field_errors.items():
        if 'not one of' in msg:
            choices = _BY_KEY[key].choices
            payload[key] = next((c for c in choices if c.lower() == str(payload[key]).lower().strip()), payload[key])
    store.save(payload)

Prevention

When it happens

Trigger: save({'proxy_mode': 'Cache'}) when choices are lowercase; passing a mode value from an older headroom version whose name was renamed; trailing whitespace or a copy-pasted synonym like 'cached'/'fast'.

Common situations: Case mismatches from hand-edited env vars; enum values renamed between releases; synonyms typed from memory instead of copied from docs.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/86fa42eaadee99b4. Report an issue: GitHub.