{"record":{"id":"0c71bc4d8644ce7c","repo":"apache/superset","slug":"found-invalid-orderby-options","errorCode":null,"errorMessage":"Found invalid orderby options","messagePattern":"Found invalid orderby options","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"superset-frontend/packages/superset-ui-core/src/query/extractQueryFields.ts","lineNumber":121,"sourceCode":"  return {\n    columns: removeDuplicates(\n      columns.filter(col => col !== ''),\n      getColumnLabel,\n    ),\n    metrics:\n      queryMode === QueryMode.Raw\n        ? undefined\n        : removeDuplicates(metrics, getMetricLabel),\n    orderby:\n      orderby.length > 0\n        ? orderby.map(item => {\n            // value can be in the format of `['[\"col1\", true]', '[\"col2\", false]'],\n            // where the option strings come directly from `order_by_choices`.\n            if (typeof item === 'string') {\n              try {\n                return JSON.parse(item);\n              } catch (error) {\n                throw new Error(t('Found invalid orderby options'));\n              }\n            }\n            return item;\n          })\n        : undefined,\n  };\n}\n","sourceCodeStart":103,"sourceCodeEnd":129,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset-frontend/packages/superset-ui-core/src/query/extractQueryFields.ts#L103-L129","documentation":"Raised by masked_encrypted_extra_validator (superset/databases/schemas.py:260) when the masked_encrypted_extra field is exactly the string \"{}\". This field round-trips the masked (redacted) form of encrypted_extra in Database API responses; submitting back an empty object means 'no change requested', which Superset rejects as an empty update rather than silently wiping the stored secrets.","triggerScenarios":"PUT /api/v1/database/<id> where the client echoes the masked value it previously read, but the database has no encrypted_extra so the masked form is \"{}\"; or a client that always includes masked_encrypted_extra in the payload, even when empty.","commonSituations":"Automation that GETs a database, mutates unrelated fields, and PUTs the whole object back including masked_encrypted_extra: \"{}\". Also forms with a hidden encrypted-extra input defaulting to \"{}\".","solutions":["Omit masked_encrypted_extra from the PUT payload when you do not intend to change it","Send null instead of \"{}\" when you want to clear or skip the field","Only send masked_encrypted_extra when replacing secrets, and send real JSON with content"],"exampleFix":"# before\nbody = {**db_response, \"masked_encrypted_extra\": \"{}\"}  # 400 Field cannot be empty.\n\n# after\nbody = {k: v for k, v in db_response.items() if k != \"masked_encrypted_extra\"}","handlingStrategy":"validation","validationCode":"def clean_masked(d: dict) -> dict:\n    me = d.get(\"masked_encrypted_extra\")\n    if me is not None and (me == \"{}\" or not me.strip()):\n        d.pop(\"masked_encrypted_extra\", None)\n    return d","typeGuard":"const hasMaskedExtra = (b: Record<string, unknown>): boolean =>\n  typeof b.masked_encrypted_extra === \"string\" && b.masked_encrypted_extra.trim() !== \"{}\" && b.masked_encrypted_extra.trim() !== \"\";","tryCatchPattern":null,"preventionTips":["Never echo masked_encrypted_extra from a GET back into a PUT","Treat masked_encrypted_extra as write-only: include it only when deliberately replacing secrets"],"tags":["superset","rest-api","validation","database-connections"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}