{"record":{"id":"f2ef56beccd86a7c","repo":"langgenius/dify","slug":"str-exc-f2ef56","errorCode":null,"errorMessage":"{str(exc)}","messagePattern":"\\{str\\(exc\\)\\}","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"api/controllers/console/agent/roster.py","lineNumber":538,"sourceCode":"        info_endpoint=f\"{base_url}/info\",\n        meta_endpoint=f\"{base_url}/meta\",\n        api_rpm=app_model.api_rpm or 0,\n        api_rph=app_model.api_rph or 0,\n        api_key_count=_agent_api_key_count(session, str(app_model.id)),\n    )\n    return response.model_dump(mode=\"json\")\n\n\ndef _agent_observability_service(session: Session) -> AgentObservabilityService:\n    return AgentObservabilityService(session)\n\n\ndef _parse_observability_time_range(start: str | None, end: str | None, account: Account):\n    timezone = account.timezone or \"UTC\"\n    try:\n        return parse_time_range(start, end, timezone)\n    except ValueError as exc:\n        abort(400, description=str(exc))\n\n\ndef _query_values(name: str, alias_name: str | None = None) -> list[str]:\n    def _get_values(field_name: str) -> list[str]:\n        values = request.args.getlist(field_name)\n        indexed_values: list[tuple[int, list[str]]] = []\n        prefix = f\"{field_name}[\"\n        for key in request.args:\n            if not key.startswith(prefix) or not key.endswith(\"]\"):\n                continue\n            index = key[len(prefix) : -1]\n            if index.isdigit():\n                indexed_values.append((int(index), request.args.getlist(key)))\n        for _, items in sorted(indexed_values):\n            values.extend(items)\n        return values\n\n    values = _get_values(name)","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/agent/roster.py#L520-L556","documentation":"Flask `abort(400, description=str(exc))` (HTTP 400) raised inside `_parse_observability_time_range` (api/controllers/console/agent/roster.py:538). It wraps `libs.datetime_utils.parse_time_range`, which raises ValueError on malformed time strings (expected format `YYYY-MM-DD HH:MM`), invalid timezone names, or when start > end. The helper uses `account.timezone or 'UTC'` as the timezone.","triggerScenarios":"An agent observability endpoint that calls `_parse_observability_time_range` receives a `start`/`end` query string that fails `parse_time_range` parsing: wrong format, unknown timezone on the account, or a start later than end.","commonSituations":"Client sends `?start=2024-13-01 10:00` (bad month), omits the time portion (`2024-01-01`), passes a swapped range, or the user's `account.timezone` is unset to an invalid tzdb name.","solutions":["Send `start` and `end` in the exact `YYYY-MM-DD HH:MM` format with URL encoding for the space.","Ensure `start <= end` and that both fall on valid calendar dates.","Verify the user's account timezone is a valid IANA name; rely on the 'UTC' fallback only if unset."],"exampleFix":"// before\nGET /agent/<id>/logs?start=2024-01-01&end=2024-02-01\n// after (correct format with encoded space)\nGET /agent/<id>/logs?start=2024-01-01%2000:00&end=2024-02-01%2000:00","handlingStrategy":"validation","validationCode":"import datetime, pytz\n\nTIME_FMT = \"%Y-%m-%d %H:%M\"\n\ndef valid_time_range(start, end, tzname=\"UTC\"):\n    tz = pytz.timezone(tzname or \"UTC\")  # raises UnknownTimeZoneError early\n    s = datetime.datetime.strptime(start, TIME_FMT) if start else None\n    e = datetime.datetime.strptime(end, TIME_FMT) if end else None\n    if s and e and s > e:\n        raise ValueError(\"start must be <= end\")\n    return True","typeGuard":"import datetime, re\n\n_TIME_RE = re.compile(r\"^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}$\")\n\ndef is_valid_range_str(value: str | None) -> bool:\n    return value is None or bool(_TIME_RE.match(value) and _parses(value))\n\ndef _parses(value):\n    try:\n        datetime.datetime.strptime(value, \"%Y-%m-%d %H:%M\")\n        return True\n    except ValueError:\n        return False","tryCatchPattern":"try:\n    resp = client.get(f\"/console/api/agent/{aid}/logs\", params={\"start\": s, \"end\": e})\nexcept HTTPError as err:\n    if err.response.status_code == 400:\n        raise ValueError(f\"bad time range: {err.response.json().get('description')}\") from err\n    raise","preventionTips":["Always URL-encode the space in `YYYY-MM-DD HH:MM`.","Confirm the user's timezone is a valid IANA name before relying on it.","Send start <= end and use the same format for both bounds."],"tags":["validation","datetime","http-400","agent","observability"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}