{"record":{"id":"43ae6964b0280bd2","repo":"apache/superset","slug":"unsupported-whisker-type-whiskeroptions","errorCode":null,"errorMessage":"Unsupported whisker type: ${whiskerOptions}","messagePattern":"Unsupported whisker type: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"superset-frontend/packages/superset-ui-chart-controls/src/operators/boxplotOperator.ts","lineNumber":52,"sourceCode":"  const { groupby, whiskerOptions } = formData;\n\n  if (whiskerOptions) {\n    let whiskerType: BoxPlotQueryObjectWhiskerType;\n    let percentiles: [number, number] | undefined;\n    const percentileMatch = PERCENTILE_REGEX.exec(whiskerOptions as string);\n\n    if (whiskerOptions === 'Tukey' || !whiskerOptions) {\n      whiskerType = 'tukey';\n    } else if (whiskerOptions === 'Min/max (no outliers)') {\n      whiskerType = 'min/max';\n    } else if (percentileMatch) {\n      whiskerType = 'percentile';\n      percentiles = [\n        parseInt(percentileMatch[1], 10),\n        parseInt(percentileMatch[2], 10),\n      ];\n    } else {\n      throw new Error(`Unsupported whisker type: ${whiskerOptions}`);\n    }\n\n    return {\n      operation: 'boxplot',\n      options: {\n        whisker_type: whiskerType,\n        percentiles,\n        groupby: ensureIsArray(groupby).map(getColumnLabel),\n        metrics: ensureIsArray(queryObject.metrics).map(getMetricLabel),\n      },\n    };\n  }\n  return undefined;\n};\n","sourceCodeStart":34,"sourceCodeEnd":67,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset-frontend/packages/superset-ui-chart-controls/src/operators/boxplotOperator.ts#L34-L67","documentation":"Raised by encrypted_extra_validator in superset/databases/schemas.py when the encrypted_extra field submitted to the Database REST API is non-empty but fails json.loads. Superset stores encrypted_extra (driver-specific secrets like credentials_params) as a JSON string, so any non-JSON payload is rejected with a marshmallow ValidationError before the DAO layer runs. The API turns this into an HTTP 400 with the JSONDecodeError detail appended to the message.","triggerScenarios":"POST /api/v1/database/ or PUT /api/v1/database/<id> with a body whose encrypted_extra is a bare string, unquoted key, trailing comma, or Python dict literal (single quotes) instead of valid JSON; also passing an already-encrypted binary blob or an empty string with stray whitespace characters.","commonSituations":"Scripts that build encrypted_extra via Python repr() instead of json.dumps(), pasting YAML/INI fragments into the field, or frontend forms serializing the object lazily. Also hit after upgrades when the encrypted_extra payload format was restructured and old tooling sends the previous shape.","solutions":["Serialize the value with json.dumps() (or JSON.stringify on the frontend) before sending it as encrypted_extra","Validate the string with json.loads() locally first; the exception message includes the exact JSONDecodeError position","If you intended no encrypted extra, send null or omit the field entirely rather than a malformed string"],"exampleFix":"# before\npayload = {\"encrypted_extra\": str({\"credentials_params\": {\"user\": \"bob\"}})}  # Python repr -> 400\n\n# after\nimport json\npayload = {\"encrypted_extra\": json.dumps({\"credentials_params\": {\"user\": \"bob\"}})}","handlingStrategy":"validation","validationCode":"import json\n\ndef valid_json_string(v: str | None) -> bool:\n    if v is None or v == \"\":\n        return True\n    try:\n        json.loads(v)\n        return True\n    except json.JSONDecodeError:\n        return False\n\nassert valid_json_string(payload.get(\"encrypted_extra\")), \"encrypted_extra must be valid JSON\"","typeGuard":"def isJsonString(v: unknown): boolean {\n  if (v == null || v === \"\") return true;\n  try { JSON.parse(v as string); return true; } catch { return false; }\n}","tryCatchPattern":"resp = requests.post(url, json=payload)\nif resp.status_code == 400 and \"cannot be decoded by JSON\" in resp.text:\n    raise ValueError(f\"encrypted_extra not valid JSON: {resp.text}\") from None","preventionTips":["Always build encrypted_extra/extra with json.dumps or JSON.stringify, never repr or template strings","Round-trip check in CI: any fixture that posts to /api/v1/database passes json.loads on its extra fields"],"tags":["superset","rest-api","json","validation","database-connections"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}