ZhuLinsen/daily_stock_analysis · error · ValueError

NOTIFICATION_QUIET_HOURS must be in HH:MM-HH:MM format

Error message

NOTIFICATION_QUIET_HOURS must be in HH:MM-HH:MM format

What it means

ValueError from parse_notification_quiet_hours when the NOTIFICATION_QUIET_HOURS environment value does not match the strict HH:MM-HH:MM pattern (24-hour clock). The value must be two zero-padded times joined by a single hyphen; anything else — missing hyphen, 12-hour times, seconds, non-numeric parts — is rejected at config load.

Source

Thrown at src/notification_noise.py:106

def normalize_notification_severity(route_type: Optional[str], severity: Optional[str] = None) -> str:
    """Normalize explicit severity, or derive a default from route type."""
    explicit = str(severity or "").strip().lower()
    if explicit in NOTIFICATION_SEVERITY_RANK:
        return explicit

    route = str(route_type or "").strip().lower()
    return DEFAULT_NOTIFICATION_SEVERITY_BY_ROUTE.get(route, "info")


def parse_notification_quiet_hours(value: Optional[str]) -> Optional[Tuple[int, int]]:
    """Parse ``HH:MM-HH:MM`` into start/end minute-of-day values."""
    raw = str(value or "").strip()
    if not raw:
        return None

    match = _QUIET_HOURS_RE.match(raw)
    if not match:
        raise ValueError("NOTIFICATION_QUIET_HOURS must be in HH:MM-HH:MM format")

    start_hour, start_minute, end_hour, end_minute = [int(group) for group in match.groups()]
    return start_hour * 60 + start_minute, end_hour * 60 + end_minute


def validate_notification_timezone(value: Optional[str]) -> None:
    """Validate an optional IANA timezone name."""
    raw = str(value or "").strip()
    if not raw:
        return
    if ZoneInfo is None:
        raise ValueError("zoneinfo is unavailable")
    try:
        ZoneInfo(raw)
    except ZoneInfoNotFoundError as exc:
        raise ValueError(f"unknown timezone: {raw}") from exc

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Use exact zero-padded 24-hour format, e.g. NOTIFICATION_QUIET_HOURS=22:00-06:00
  2. Remove spaces around the hyphen and any seconds/trailing components
  3. Unset the variable entirely if you do not want quiet hours — empty is valid and disables the feature
  4. Check for invisible characters (smart quotes, non-breaking spaces) after pasting into .env

Example fix

# before
NOTIFICATION_QUIET_HOURS=10pm - 6am

# after
NOTIFICATION_QUIET_HOURS=22:00-06:00
Defensive patterns

Strategy: validation

Validate before calling

import re

_QUIET = re.compile(r"^([01]\d|2[0-3]):([0-5]\d)-([01]\d|2[0-3]):([0-5]\d)$")

def is_valid_quiet_hours(value: str) -> bool:
    return bool(_QUIET.match((value or "").strip()))

Try / catch

try:
    quiet = parse_notification_quiet_hours(os.getenv("NOTIFICATION_QUIET_HOURS"))
except ValueError as exc:
    log.error("Config error: %s (expected HH:MM-HH:MM, e.g. 22:00-06:00)", exc)
    raise SystemExit(2)

Prevention

When it happens

Trigger: Setting NOTIFICATION_QUIET_HOURS to values like '22:00', '22:00 - 06:00' (spaces), '9pm-6am', '22:0-06:00', '22:00-06:00:30', or '22-06'. Only strings fully matching HH:MM-HH:MM with hours 00-23 and minutes 00-59 (per the regex) parse successfully.

Common situations: Hand-editing .env with a human-friendly time range; copying a 12-hour format from documentation of another tool; locale-specific time strings; trailing whitespace or smart quotes after copy-paste from a chat or doc.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/73e752296192ed75. Report an issue: GitHub.