HKUDS/Vibe-Trading · error · ValueError

timezone must be a non-empty IANA timezone key or null

Error message

timezone must be a non-empty IANA timezone key or null

What it means

validate_timezone_shape only checks the shape of the optional tz field on a scheduled job: it must be None (meaning legacy UTC) or a non-empty, non-whitespace string. The actual IANA resolution is deliberately deferred to validate_timezone so shape errors are caught even on hosts without the tz database.

Source

Thrown at agent/src/scheduled_research/models.py:154

    if len(parts) != _CRON_PARTS:
        raise ValueError(f"schedule must be a positive integer (ms) or a 5-field cron string; got: {schedule!r}")
    for part, (low, high) in zip(parts, CRON_BOUNDS):
        _validate_cron_field(part, low, high)


def validate_timezone_shape(tz: Optional[str]) -> None:
    """Raise ``ValueError`` when *tz* is not ``None`` or a non-empty string.

    This is the persistence-level check: it deliberately does NOT resolve the
    key, because resolvability depends on the host's timezone database. A
    store written where a key resolved must keep loading and persisting on a
    host where it does not; the executor surfaces the unresolvable key as a
    per-job schedule failure instead.
    """
    if tz is None:
        return
    if not isinstance(tz, str) or not tz.strip():
        raise ValueError("timezone must be a non-empty IANA timezone key or null")


def validate_timezone(tz: Optional[str]) -> None:
    """Raise ``ValueError`` when *tz* is not a resolvable IANA timezone key.

    ``None`` is always valid and means legacy UTC semantics. Resolution goes
    through :class:`zoneinfo.ZoneInfo`, so whatever validates here is exactly
    what the executor can evaluate later.

    Args:
        tz: An IANA timezone key such as ``"Pacific/Auckland"``, or ``None``.

    Raises:
        ValueError: When *tz* is not ``None`` and cannot be resolved.
    """
    validate_timezone_shape(tz)
    if tz is None:
        return

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Send null (or omit the field) for UTC semantics; send a real key like 'America/New_York' otherwise.
  2. Normalize empty strings to None before calling the API: tz = tz or None.
  3. Add a form/API-level check that timezone is null or a populated IANA string.

Example fix

# before
create_scheduled_run(..., tz='')
# after
create_scheduled_run(..., tz=None)  # or tz='America/New_York'
Defensive patterns

Strategy: validation

Validate before calling

tz = tz.strip() if isinstance(tz, str) else None
if not tz:
    tz = None  # empty means UTC/legacy semantics
create_scheduled_run(..., tz=tz)

Type guard

def is_valid_tz_shape(tz) -> bool:
    return tz is None or (isinstance(tz, str) and bool(tz.strip()))

Try / catch

try:
    create_scheduled_run(..., tz=tz)
except ValueError as e:
    if 'timezone' in str(e):
        return bad_request(str(e))
    raise

Prevention

When it happens

Trigger: create_scheduled_run(tz='') or tz=' ' (whitespace), tz=123 or tz=[] (non-string), or a JSON body where 'tz': null vs missing-string handling forwards an empty value.

Common situations: Frontend forms submitting an empty timezone field as '' instead of omitting it; defaults like tz=os.getenv('JOB_TZ', '') that produce empty strings; JSON schemas typing tz loosely.

Understand the failure class

Background: "Invalid configuration value" and "Unsupported/Unknown setting value" errors: why libraries reject your config strings, numbers, and types — this error's family across 30 libraries.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/dd9a1a421a2159aa. Report an issue: GitHub.