ruvnet/RuView · error · ValueError

invalid privacy class {s!r}; expected one of {list(m.keys())

Error message

invalid privacy class {s!r}; expected one of {list(m.keys())}

What it means

PrivacyClass.from_str parses the --privacy-class CLI/config value into the integer privacy classes used by the ADR-125 §2.1.d HomeKit/HAP gate. Only raw, derived, anonymous, restricted (case-insensitive) are accepted; any other string raises ValueError listing the valid set. Only anonymous (2) and restricted (3) may cross the HAP boundary (allows_hap).

Source

Thrown at scripts/c6-presence-watcher.py:82

    """
    RAW = 0
    DERIVED = 1
    ANONYMOUS = 2
    RESTRICTED = 3

    _names = {RAW: "Raw", DERIVED: "Derived", ANONYMOUS: "Anonymous",
              RESTRICTED: "Restricted"}

    @classmethod
    def name(cls, value: int) -> str:
        return cls._names.get(value, f"Unknown({value})")

    @classmethod
    def from_str(cls, s: str) -> int:
        m = {"raw": cls.RAW, "derived": cls.DERIVED,
             "anonymous": cls.ANONYMOUS, "restricted": cls.RESTRICTED}
        if s.lower() not in m:
            raise ValueError(f"invalid privacy class {s!r}; "
                             f"expected one of {list(m.keys())}")
        return m[s.lower()]

    @classmethod
    def allows_hap(cls, value: int) -> bool:
        """ADR-125 §2.1.d gate: only class-2/3 cross the HomeKit boundary."""
        return value in (cls.ANONYMOUS, cls.RESTRICTED)


# Semantic-event naming per ADR-125 §2.1.d. The HAP bridge keeps
# advertising a generic MotionSensor; this is the operator-facing
# *label* for the event, written into the watcher log + summary line
# so the operator never sees "intruder detected" framing.
SEMANTIC_EVENT_UNKNOWN_PRESENCE = "Unknown Presence"

# Hysteresis — entry / exit thresholds keep the HomeKit characteristic
# from flapping when presence_score sits near the boundary.
PRESENCE_ON_THRESHOLD = 0.40

View on GitHub (pinned to 4685618388)

Solutions

  1. Use one of raw, derived, anonymous, restricted (the CLI default is anonymous)
  2. For HomeKit exposure choose anonymous or restricted — raw/derived fail the allows_hap gate and the watcher refuses to start
  3. Fix the calling script/config that produced the invalid token

Example fix

# before
python scripts/c6-presence-watcher.py --privacy-class public  # ValueError

# after
python scripts/c6-presence-watcher.py --privacy-class anonymous
Defensive patterns

Strategy: validation

Validate before calling

VALID_PRIVACY = {"raw", "derived", "anonymous", "restricted"}

def validate_privacy_class(value: str) -> str:
    if value.lower() not in VALID_PRIVACY:
        raise SystemExit(
            f"invalid privacy class {value!r}; "
            f"expected one of {sorted(VALID_PRIVACY)}"
        )
    return value.lower()

Type guard

def is_valid_privacy_class(s: str) -> bool:
    return isinstance(s, str) and s.lower() in {
        "raw", "derived", "anonymous", "restricted"
    }

Try / catch

try:
    privacy_class = PrivacyClass.from_str(raw_value)
except ValueError as e:
    raise SystemExit(f"bad --privacy-class value: {e}") from e

Prevention

When it happens

Trigger: `python scripts/c6-presence-watcher.py --privacy-class public` (or 'anon', 'PII', any non-listed token); an automation or config file feeding an old/renamed class name into from_str at line 213.

Common situations: Renamed classes after an ADR update; typos in wrapper scripts; runbooks with stale example commands.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/93b72b7bede0ba7a. Report an issue: GitHub.