{"record":{"id":"2cfee77cda81bc05","repo":"bytebase/bytebase","slug":"invalid-order-by-entry-q","errorCode":null,"errorMessage":"invalid order_by entry %q","messagePattern":"invalid order_by entry %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/store/common.go","lineNumber":54,"sourceCode":"\tSortOrder SortOrder\n}\n\n// getOrderByKeys parses an AIP-132 order_by string against a whitelist\n// mapping API field names to SQL columns. Strict where parseOrderBy is not:\n// every comma-separated entry must be a whitelisted field with an optional\n// \"asc\"/\"desc\" suffix, and a repeated field is rejected — malformed input\n// errors instead of being silently reinterpreted.\nfunc getOrderByKeys(orderBy string, columns map[string]string) ([]*OrderByKey, error) {\n\tif orderBy == \"\" {\n\t\treturn nil, nil\n\t}\n\n\tvar result []*OrderByKey\n\tseen := make(map[string]bool)\n\tfor entry := range strings.SplitSeq(orderBy, \",\") {\n\t\tparts := strings.Fields(entry)\n\t\tif len(parts) == 0 || len(parts) > 2 {\n\t\t\treturn nil, errors.Errorf(\"invalid order_by entry %q\", strings.TrimSpace(entry))\n\t\t}\n\t\tcolumn, ok := columns[parts[0]]\n\t\tif !ok {\n\t\t\treturn nil, errors.Errorf(\"unsupported order field %q\", parts[0])\n\t\t}\n\t\tif seen[parts[0]] {\n\t\t\treturn nil, errors.Errorf(\"duplicate order field %q\", parts[0])\n\t\t}\n\t\tseen[parts[0]] = true\n\t\tsortOrder := ASC\n\t\tif len(parts) == 2 {\n\t\t\tswitch parts[1] {\n\t\t\tcase \"asc\":\n\t\t\tcase \"desc\":\n\t\t\t\tsortOrder = DESC\n\t\t\tdefault:\n\t\t\t\treturn nil, errors.Errorf(\"invalid order direction %q, expect asc or desc\", parts[1])\n\t\t\t}","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/store/common.go#L36-L72","documentation":"getOrderByKeys parses a comma-separated order_by string where each entry must be 'column' or 'column asc|desc' (at most two whitespace-separated parts). An entry that is empty after splitting or has more than two tokens (e.g. an embedded third word) is rejected with this error quoting the raw entry.","triggerScenarios":"order_by strings like \"create_time desc asc\", \"create_time  desc extra\", \"update_time DESC, \" (trailing comma yielding an empty entry) passed to GetSavedQueryOrders or other consumers.","commonSituations":"Trailing commas in hand-built order_by; users typing 'order by create time descending' in prose form; double spaces creating empty tokens when combined with stray words; programmatic builders appending both direction words.","solutions":["Use entries of the form 'column' or 'column asc|desc' separated by single commas, e.g. \"update_time desc, create_time\".","Strip trailing commas and collapse whitespace before sending.","Only use columns from the endpoint's allowed set (this specific message is about token shape; a bad column yields the separate 'unsupported order field' error)."],"exampleFix":"// before\norderBy := \"create_time desc asc, \"   // 3 tokens + empty entry\n// after\norderBy := \"create_time desc\"","handlingStrategy":"validation","validationCode":"const entries = orderBy.split(\",\");\nfor (const e of entries) {\n  const parts = e.trim().split(/\\s+/).filter(Boolean);\n  if (parts.length === 0 || parts.length > 2) throw new Error(`invalid order_by entry: \"${e.trim()}\"`);\n}","typeGuard":null,"tryCatchPattern":"try { return await listSavedQueries({ orderBy }); } catch (e) { if (String(e).includes(\"invalid order_by entry\")) { throw new Error(\"Each entry must be 'column' or 'column asc|desc'\"); } throw e; }","preventionTips":["Trim each entry and collapse internal whitespace before sending","Strip trailing commas from programmatic order_by strings","Emit at most one direction word (asc/desc) per column","Build order_by from a typed array of {column, direction} instead of raw strings"],"tags":["ordering","validation","query-parameter"],"backgroundTag":"invalid-argument-format","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}