HKUDS/Vibe-Trading · error · ValueError

end_at must be a valid RFC3339 timestamp

Error message

end_at must be a valid RFC3339 timestamp

What it means

The end_at string could not be parsed by datetime.fromisoformat (after replacing a trailing 'Z' with '+00:00'). The value must be a valid RFC3339/ISO-8601 timestamp.

Source

Thrown at agent/src/scheduled_research/service.py:89

            executor = None
    if executor is not None:
        running = bool(executor.is_running)
    return {"enabled": enabled, "running": running, "executable": enabled and running}


def _parse_end_at(value: Any) -> int | None:
    if value in (None, ""):
        return None
    if isinstance(value, bool):
        raise ValueError("end_at must be RFC3339 text or epoch milliseconds")
    if isinstance(value, int):
        return value
    if not isinstance(value, str):
        raise ValueError("end_at must be RFC3339 text or epoch milliseconds")
    try:
        parsed = datetime.fromisoformat(value.strip().replace("Z", "+00:00"))
    except ValueError as exc:
        raise ValueError("end_at must be a valid RFC3339 timestamp") from exc
    if parsed.tzinfo is None:
        raise ValueError("end_at must include an explicit timezone offset")
    return int(parsed.timestamp() * 1000)


def _origin_target(session_id: str | None) -> tuple[str, str, str | None, str]:
    if not session_id:
        raise ValueError("origin delivery requires the originating session")
    host = sys.modules.get("api_server") or sys.modules.get("agent.api_server")
    service = (
        host._get_session_service()
        if host and hasattr(host, "_get_session_service")
        else None
    )
    session = service.get_session(session_id) if service else None
    config = getattr(session, "config", None) or {}
    channel = config.get("channel")
    target = config.get("channel_chat_id")

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Use strict RFC3339 with offset: '2026-12-31T00:00:00+00:00' or 'Z' suffix
  2. Pre-validate/normalize user input (dateutil, date-fns) before submitting the draft
  3. Send epoch milliseconds instead if string formatting is fragile in your stack

Example fix

// before
{"end_at": "31/12/2026"}
// after
{"end_at": "2026-12-31T00:00:00Z"}
Defensive patterns

Strategy: validation

Validate before calling

from datetime import datetime

def ok(ts: str) -> bool:
    try:
        datetime.fromisoformat(ts.strip().replace("Z", "+00:00"))
        return True
    except ValueError:
        return False
assert ok(draft["end_at"])

Type guard

def is_rfc3339(s: str) -> bool:
    try:
        datetime.fromisoformat(s.strip().replace("Z", "+00:00"))
        return True
    except ValueError:
        return False

Try / catch

try:
    build_job_from_draft(draft)
except ValueError as e:
    if "valid RFC3339" in str(e):
        prompt_user_for_date()

Prevention

When it happens

Trigger: Passing strings like '2026-13-01', 'tomorrow', '12/31/2026', or a malformed ISO string; locale-formatted dates; Python <3.11 rejecting some ISO variants that fromisoformat doesn't support.

Common situations: Human-entered dates from chat/UI not normalized; Python versions prior to 3.11 where fromisoformat is strict about formats; copying dates with unusual separators.

Related errors


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