apache/superset · error · Error

Unsupported whisker type: ${whiskerOptions}

Error message

Unsupported whisker type: ${whiskerOptions}

What it means

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.

Source

Thrown at superset-frontend/packages/superset-ui-chart-controls/src/operators/boxplotOperator.ts:52

  const { groupby, whiskerOptions } = formData;

  if (whiskerOptions) {
    let whiskerType: BoxPlotQueryObjectWhiskerType;
    let percentiles: [number, number] | undefined;
    const percentileMatch = PERCENTILE_REGEX.exec(whiskerOptions as string);

    if (whiskerOptions === 'Tukey' || !whiskerOptions) {
      whiskerType = 'tukey';
    } else if (whiskerOptions === 'Min/max (no outliers)') {
      whiskerType = 'min/max';
    } else if (percentileMatch) {
      whiskerType = 'percentile';
      percentiles = [
        parseInt(percentileMatch[1], 10),
        parseInt(percentileMatch[2], 10),
      ];
    } else {
      throw new Error(`Unsupported whisker type: ${whiskerOptions}`);
    }

    return {
      operation: 'boxplot',
      options: {
        whisker_type: whiskerType,
        percentiles,
        groupby: ensureIsArray(groupby).map(getColumnLabel),
        metrics: ensureIsArray(queryObject.metrics).map(getMetricLabel),
      },
    };
  }
  return undefined;
};

View on GitHub (pinned to f4587218dd)

Solutions

  1. Serialize the value with json.dumps() (or JSON.stringify on the frontend) before sending it as encrypted_extra
  2. Validate the string with json.loads() locally first; the exception message includes the exact JSONDecodeError position
  3. If you intended no encrypted extra, send null or omit the field entirely rather than a malformed string

Example fix

# before
payload = {"encrypted_extra": str({"credentials_params": {"user": "bob"}})}  # Python repr -> 400

# after
import json
payload = {"encrypted_extra": json.dumps({"credentials_params": {"user": "bob"}})}
Defensive patterns

Strategy: validation

Validate before calling

import json

def valid_json_string(v: str | None) -> bool:
    if v is None or v == "":
        return True
    try:
        json.loads(v)
        return True
    except json.JSONDecodeError:
        return False

assert valid_json_string(payload.get("encrypted_extra")), "encrypted_extra must be valid JSON"

Type guard

def isJsonString(v: unknown): boolean {
  if (v == null || v === "") return true;
  try { JSON.parse(v as string); return true; } catch { return false; }
}

Try / catch

resp = requests.post(url, json=payload)
if resp.status_code == 400 and "cannot be decoded by JSON" in resp.text:
    raise ValueError(f"encrypted_extra not valid JSON: {resp.text}") from None

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/superset@f4587218dd (2026-08-14). Data as JSON: /api/errors/43ae6964b0280bd2. Report an issue: GitHub.