headroomlabs-ai/headroom · error · ValueError

target_savings must be between 0 and 1

Error message

target_savings must be between 0 and 1

What it means

with_target_savings rejects any target_savings outside the open interval (0, 1). The check is strict (0 < x < 1), so exactly 0 or exactly 1 are also rejected, because a 0% or 100% savings target produces a degenerate target_ratio (1.0 or 0.0) that downstream budget math cannot use meaningfully.

Source

Thrown at headroom/agent_savings.py:380

    Deliberately NOT called from ``_proxy_config_from_env`` / ``ContentRouter`` or
    any other library-level builder that unit tests construct directly, so those
    keep clean (unseeded) defaults and test isolation is preserved.
    """
    target = os.environ if env is None else env
    # apply_agent_savings_env_defaults honors an explicit HEADROOM_SAVINGS_PROFILE
    # already in the env and otherwise falls back to DEFAULT_PROFILE (coding).
    apply_agent_savings_env_defaults(target)


def with_target_savings(
    profile: AgentSavingsProfile,
    target_savings: float,
) -> AgentSavingsProfile:
    """Return a copy of ``profile`` adjusted to a specific savings target."""

    if not 0 < target_savings < 1:
        raise ValueError("target_savings must be between 0 and 1")
    return replace(
        profile,
        target_savings=target_savings,
        target_ratio=round(1 - target_savings, 4),
    )

View on GitHub (pinned to 322425c43b)

Solutions

  1. Pass a fraction strictly between 0 and 1: 0.5 means 50% savings.
  2. If your input is a percentage, divide by 100 and clamp to the open interval before calling: max(min(pct/100, 0.999), 0.001).
  3. If you genuinely need 'no savings' or 'full savings', pick a boundary-adjacent value (e.g. 0.0001) or bypass this helper, since the function intentionally forbids the exact endpoints.

Example fix

# before
profile = with_target_savings(profile, float(os.environ["SAVINGS_PCT"]))  # 75 -> ValueError

# after
pct = float(os.environ["SAVINGS_PCT"]) / 100
profile = with_target_savings(profile, min(max(pct, 0.001), 0.999))
Defensive patterns

Strategy: validation

Validate before calling

def _clamp_target_savings(value: float) -> float:
    """Coerce to the open interval (0, 1) required by with_target_savings."""
    if not 0 < value < 1:
        raise ValueError(f"target_savings={value!r} must be in the open interval (0, 1)")
    return value

# use before the call:
target = float(os.environ.get("HEADROOM_SAVINGS", "0.5")) / 100 if float(os.environ.get("HEADROOM_SAVINGS", "50")) > 1 else float(os.environ.get("HEADROOM_SAVINGS", "0.5"))
_clamp_target_savings(target)

Type guard

def is_valid_target_savings(x: object) -> bool:
    return isinstance(x, (int, float)) and not isinstance(x, bool) and 0 < x < 1

Try / catch

try:
    profile = with_target_savings(profile, target)
except ValueError as e:
    if "target_savings" in str(e):
        raise SystemExit(f"--savings must be a fraction in (0,1), got {target!r}; did you mean {target/100}?" )
    raise

Prevention

When it happens

Trigger: Calling with_target_savings(profile, 0.0), with_target_savings(profile, 1.0), a negative value, or a value greater than 1. Values like 0.05 or 0.95 are fine.

Common situations: Computing target_savings from a CLI flag or env var without bounds-checking ('--savings 100' meaning 100 percent instead of 1.0), integer division yielding 0, or treating the parameter as inclusive 0..=1.

Related errors


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