langgenius/dify · error
{str(e)}
Error message
{str(e)} What it means
HTTP 400 returned by the daily workflow runs statistics endpoint (GET /apps/<app>/workflow/runs/daily). The handler wraps `libs.datetime_utils.parse_time_range` in a try/except ValueError and re-emits the exception text via `abort(400, description=str(e))`. `parse_time_range` raises ValueError for malformed time strings (expected `YYYY-MM-DD HH:MM`), unparseable timezones, or when `start > end`.
Source
Thrown at api/controllers/console/app/workflow_statistic.py:116
200,
"Daily runs statistics retrieved successfully",
console_ns.models[WorkflowDailyRunsStatisticResponse.__name__],
)
@setup_required
@login_required
@account_initialization_required
@with_current_user
@rbac_permission_required(RBACResourceScope.APP, RBACPermission.APP_MONITOR)
@get_app_model
@model_validate(WorkflowStatisticQuery)
def get(self, req_data: WorkflowStatisticQuery, account: Account, app_model: App):
assert account.timezone is not None
try:
start_date, end_date = parse_time_range(req_data.start, req_data.end, account.timezone)
except ValueError as e:
abort(400, description=str(e))
response_data = self._workflow_run_repo.get_daily_runs_statistics(
tenant_id=app_model.tenant_id,
app_id=app_model.id,
triggered_from=WorkflowRunTriggeredFrom.APP_RUN,
start_date=start_date,
end_date=end_date,
timezone=account.timezone,
)
return jsonify({"data": response_data})
@console_ns.route("/apps/<uuid:app_id>/workflow/statistics/daily-terminals")
class WorkflowDailyTerminalsStatistic(Resource):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
session_maker = sessionmaker(bind=db.engine, expire_on_commit=False)View on GitHub (pinned to ef8544b173)
Solutions
- Format both `start` and `end` query params as `YYYY-MM-DD HH:MM` (e.g. `2024-08-12 09:30`) in the client before sending.
- Validate client-side that `start <= end` before issuing the request.
- Confirm the authenticated account has a valid IANA timezone string in `account.timezone` (set in account profile/settings).
- If you need ISO 8601 / date-only input, normalize to `YYYY-MM-DD HH:MM` at the API boundary rather than relying on the default format.
Example fix
// before
fetch(`/apps/${appId}/workflow/runs/daily?start=2024-08-01T00:00:00Z&end=2024-08-12`)
// after
const fmt = (d: Date) => d.toISOString().slice(0,16).replace('T',' ')
fetch(`/apps/${appId}/workflow/runs/daily?start=${fmt(start)}&end=${fmt(end)}`) Defensive patterns
Strategy: validation
Validate before calling
import datetime, pytz
def valid_range(start: str | None, end: str | None, tzname: str) -> bool:
fmt = "%Y-%m-%d %H:%M"
s = datetime.datetime.strptime(start, fmt) if start else None
e = datetime.datetime.strptime(end, fmt) if end else None
pytz.timezone(tzname) # raises if invalid
return not (s and e and s > e)
# call before GET /apps/<id>/workflow/runs/daily
if not valid_range(req.start, req.end, account.timezone):
raise ValueError("bad range") Type guard
from typing import Optional
import re
DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$")
def is_workflow_stat_time(v: Optional[str]) -> bool:
return v is None or bool(DATE_RE.match(v)) Try / catch
try:
resp = client.get(f"/apps/{app}/workflow/runs/daily", params={"start": s, "end": e})
except HTTPError as err:
if err.response.status_code == 400:
# parse_time_range failed: fix format/order and retry once
...
raise Prevention
- Always format workflow-statistic dates as YYYY-MM-DD HH:MM in the client.
- Validate start <= end before sending.
- Centralize date formatting in one shared client util across all four statistic tabs.
- Ensure account.timezone is set to a valid IANA zone.
When it happens
Trigger: Calling GET /console/api/apps/<app_id>/workflow/runs/daily?start=...&end=... with a start/end value that fails `datetime.strptime(value, "%Y-%m-%d %H:%M")`, or with `start` later than `end`. The account timezone comes from `account.timezone` and must be a valid pytz zone; an unknown zone raises from `pytz.timezone(tzname)`.
Common situations: Frontend sending ISO 8601 (`2024-01-01T00:00:00Z`) or date-only strings instead of `YYYY-MM-DD HH:MM`; date picker returning end-before-start ranges; account record with a null/invalid `timezone` (the `assert account.timezone is not None` just above would actually trip first as an AssertionError, but a non-None invalid tz hits this path).
Related errors
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/579b12cac330def4.
Report an issue: GitHub.