{"record":{"id":"c43eb5b7dfb3da3e","repo":"langgenius/dify","slug":"sort-order-must-be-asc-or-desc","errorCode":null,"errorMessage":"sort_order must be asc or desc","messagePattern":"sort_order must be asc or desc","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"api/controllers/console/agent/roster.py","lineNumber":220,"sourceCode":"            return []\n        if isinstance(value, list):\n            return [str(item).strip() for item in value if str(item).strip()]\n        raise ValueError(\"Unsupported query list type.\")\n\n    @field_validator(\"sort_by\")\n    @classmethod\n    def validate_sort_by(cls, value: str) -> str:\n        normalized = value.strip().lower()\n        if normalized not in {\"created_at\", \"updated_at\"}:\n            raise ValueError(\"sort_by must be created_at or updated_at\")\n        return normalized\n\n    @field_validator(\"sort_order\")\n    @classmethod\n    def validate_sort_order(cls, value: str) -> str:\n        normalized = value.strip().lower()\n        if normalized not in {\"asc\", \"desc\"}:\n            raise ValueError(\"sort_order must be asc or desc\")\n        return normalized\n\n\nclass AgentStatisticsQuery(BaseModel):\n    source: str | None = Field(\n        default=None,\n        description=(\n            \"Filter by a structured webapp:<app_id> or workflow:<app_id> source ID. \"\n            \"Legacy invoke sources and exact workflow version/node source IDs remain supported.\"\n        ),\n    )\n    start: str | None = Field(default=None, description=\"Start date (YYYY-MM-DD HH:MM)\")\n    end: str | None = Field(default=None, description=\"End date (YYYY-MM-DD HH:MM)\")\n\n    @field_validator(\"source\", \"start\", \"end\", mode=\"before\")\n    @classmethod\n    def empty_string_to_none(cls, value: str | None) -> str | None:\n        if value == \"\":","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/agent/roster.py#L202-L238","documentation":"A Pydantic field_validator on RosterListQuery.sort_order: the value (trimmed, lowercased) must be one of {asc, desc}. Any other value is rejected with a 422. The validator normalizes case first, so 'DESC' or ' Asc ' are accepted but 'ascending' or '1' are not.","triggerScenarios":"GET /console/agents with ?sort_order=<anything else>, e.g. ?sort_order=ascending, ?sort_order=1, ?sort_order=-1. Common when a UI toggles pass numeric or long-form direction tokens.","commonSituations":"Frontend that emits 'ascending'/'descending' or 1/-1 from a sort toggle; a generic grid component that passes its own direction vocabulary; typo.","solutions":["Send ?sort_order=asc or ?sort_order=desc (case-insensitive, trimmed).","Map the UI's direction token to asc/desc before submitting.","Omit sort_order to use the default (desc).","Update the client's sort-order vocabulary from the schema."],"exampleFix":"// before\nquery.sort_order = 'ascending'\n// after\nquery.sort_order = 'asc'","handlingStrategy":"validation","validationCode":"ALLOWED_SORT_ORDER = {'asc', 'desc'}\n\ndef normalize_sort_order(value: str) -> str:\n    norm = (value or '').strip().lower()\n    return norm if norm in ALLOWED_SORT_ORDER else 'desc'","typeGuard":"def is_valid_sort_order(value: str) -> bool:\n    return isinstance(value, str) and value.strip().lower() in {'asc', 'desc'}","tryCatchPattern":null,"preventionTips":["Map UI direction tokens (ascending/1, descending/-1) to asc/desc.","Omit sort_order to fall back to the default (desc).","Centralize the sort-order vocabulary in one client constant."],"tags":["pydantic","validation","query-params","agent-roster","sorting"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}