{"record":{"id":"516a8f5075e0c40c","repo":"HKUDS/Vibe-Trading","slug":"end-at-must-include-an-explicit-timezone-offset","errorCode":null,"errorMessage":"end_at must include an explicit timezone offset","messagePattern":"end_at must include an explicit timezone offset","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"agent/src/scheduled_research/service.py","lineNumber":91,"sourceCode":"        running = bool(executor.is_running)\n    return {\"enabled\": enabled, \"running\": running, \"executable\": enabled and running}\n\n\ndef _parse_end_at(value: Any) -> int | None:\n    if value in (None, \"\"):\n        return None\n    if isinstance(value, bool):\n        raise ValueError(\"end_at must be RFC3339 text or epoch milliseconds\")\n    if isinstance(value, int):\n        return value\n    if not isinstance(value, str):\n        raise ValueError(\"end_at must be RFC3339 text or epoch milliseconds\")\n    try:\n        parsed = datetime.fromisoformat(value.strip().replace(\"Z\", \"+00:00\"))\n    except ValueError as exc:\n        raise ValueError(\"end_at must be a valid RFC3339 timestamp\") from exc\n    if parsed.tzinfo is None:\n        raise ValueError(\"end_at must include an explicit timezone offset\")\n    return int(parsed.timestamp() * 1000)\n\n\ndef _origin_target(session_id: str | None) -> tuple[str, str, str | None, str]:\n    if not session_id:\n        raise ValueError(\"origin delivery requires the originating session\")\n    host = sys.modules.get(\"api_server\") or sys.modules.get(\"agent.api_server\")\n    service = (\n        host._get_session_service()\n        if host and hasattr(host, \"_get_session_service\")\n        else None\n    )\n    session = service.get_session(session_id) if service else None\n    config = getattr(session, \"config\", None) or {}\n    channel = config.get(\"channel\")\n    target = config.get(\"channel_chat_id\")\n    if not isinstance(channel, str) or not isinstance(target, str):\n        raise ValueError(\"the originating session is not an IM conversation\")","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/service.py#L73-L109","documentation":"The end_at timestamp parsed successfully but carried no timezone information (no 'Z' suffix or ±HH:MM offset). Because the value is converted to epoch milliseconds, an implicit local-time interpretation would be ambiguous, so an explicit offset is required.","triggerScenarios":"Passing '2026-12-31T00:00:00' (naive ISO string) without offset; clients stripping the timezone during serialization; date libraries defaulting to naive datetimes.","commonSituations":"Python datetime.isoformat() on a naive datetime; JavaScript date formatting that drops the zone; users in non-UTC zones getting silently shifted times, hence the strict rule.","solutions":["Append the offset: use datetime.now(timezone.utc).isoformat() or add 'Z'","Configure serializers to always include the zone","Or pass epoch milliseconds, which are inherently UTC"],"exampleFix":"# before\ndatetime(2026,12,31).isoformat()  # naive\n# after\ndatetime(2026,12,31,tzinfo=timezone.utc).isoformat()  # '2026-12-31T00:00:00+00:00'","handlingStrategy":"validation","validationCode":"from datetime import datetime\n\ndef has_tz(s: str) -> bool:\n    try:\n        return datetime.fromisoformat(s.strip().replace(\"Z\", \"+00:00\")).tzinfo is not None\n    except ValueError:\n        return False\nassert has_tz(draft[\"end_at\"])","typeGuard":"def is_timezone_aware_rfc3339(s: str) -> bool:\n    try:\n        return datetime.fromisoformat(s.strip().replace(\"Z\", \"+00:00\")).tzinfo is not None\n    except ValueError:\n        return False","tryCatchPattern":"try:\n    build_job_from_draft(draft)\nexcept ValueError as e:\n    if \"explicit timezone\" in str(e):\n        draft[\"end_at\"] += \"+00:00\"  # or re-format as UTC","preventionTips":["Always produce timestamps with datetime.now(timezone.utc) or ISO strings ending in Z","Treat naive datetimes as a bug in client serialization"],"tags":["scheduled-research","validation","datetime","timezone"],"backgroundTag":"naive-datetime-missing-timezone","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}