headroomlabs-ai/headroom · error · ValueError
expected a boolean, got {value!r}
Error message
expected a boolean, got {value!r} What it means
ValueError from _coerce in headroom/settings_store.py when a field of type 'bool' or 'optional-bool' receives a value that is neither a real bool nor a string token in {1,true,yes,on} / {0,false,no,off,''}. Note the check is case-insensitive and strips whitespace, so ' True ' is fine, but anything else ('maybe', '2', 'enabled') raises. The ValueError is collected into SettingsValidationError.field_errors by the caller.
Source
Thrown at headroom/settings_store.py:773
"""Coerce a raw JSON/env value to the field's Python type.
Returns ``None`` for null and empty values (empty coerces to ``None`` for
every type except a plain ``bool``, which becomes ``False``). Raises
``ValueError`` on bad input so callers can surface a per-field message.
"""
if value is None:
return None
if field.type in ("bool", "optional-bool"):
if isinstance(value, bool):
return value
token = str(value).strip().lower()
if field.type == "optional-bool" and token == "":
return None
if token in ("1", "true", "yes", "on"):
return True
if token in ("0", "false", "no", "off", ""):
return False
raise ValueError(f"expected a boolean, got {value!r}")
if field.type in ("int", "float"):
if isinstance(value, bool): # bool is an int subclass — reject explicitly
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":View on GitHub (pinned to 322425c43b)
Solutions
- Change the value to one of the accepted tokens: true/false, 1/0, yes/no, on/off (case-insensitive), or a real JSON boolean.
- If the value legitimately may be absent, make sure the field is optional-bool and pass null/'' rather than a placeholder word.
- If you must accept other spellings, normalize them to true/false in your own code before calling the settings API.
Example fix
# before
save({'proxy_optimize': 'ENABLED'}) # ValueError: expected a boolean, got 'ENABLED'
# after
save({'proxy_optimize': 'on'}) # or True / 'true' Defensive patterns
Strategy: validation
Validate before calling
TRUE = {'1', 'true', 'yes', 'on'}
FALSE = {'0', 'false', 'no', 'off', ''}
def valid_bool_token(v) -> bool:
return isinstance(v, bool) or str(v).strip().lower() in TRUE | FALSE Type guard
def is_bool_like(v) -> bool:
return isinstance(v, bool) or str(v).strip().lower() in {'1','true','yes','on','0','false','no','off',''} Try / catch
from headroom.settings_store import SettingsValidationError
try:
store.save(payload)
except SettingsValidationError as e:
for key, msg in e.field_errors.items():
if 'expected a boolean' in msg:
payload[key] = bool(payload[key]) # or fix upstream
store.save(payload) Prevention
- Use plain true/false in JSON payloads.
- Document the accepted env tokens next to every boolean you expose to operators.
When it happens
Trigger: Saving a bool setting with an unsupported string (HEADROOM-style env value 'ENABLED', 'y', '2') or a non-string non-bool JSON value like 2 or [True]. Example: save({'optimize': 'sometimes'}) where 'optimize' is a bool field.
Common situations: Operators copying boolean conventions from other tools ('y'/'enabled'/'on-request'); JSON payloads from UIs that send 1/2 instead of true/false; shell scripts passing $FLAG that is empty-but-quoted oddly combined with a plain bool type (empty string maps to False for 'bool' but None for 'optional-bool').
Related errors
- expected a number, got {value!r}
- expected an integer, got {value!r}
- expected a finite number, got {value!r}
- must be >= {field.minimum}
- must be <= {field.maximum}
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/60f6f250806112e6.
Report an issue: GitHub.