{"record":{"id":"0900f18057cec3d9","repo":"apache/superset","slug":"not-a-valid-list","errorCode":null,"errorMessage":"Not a valid list","messagePattern":"Not a valid list","errorType":"validation","errorClass":"ValidationError","httpStatus":422,"severity":"error","filePath":"superset/dashboards/schemas.py","lineNumber":195,"sourceCode":"    \"\"\"\n    A custom field that accepts either a list of strings or a dictionary.\n    \"\"\"\n\n    def _deserialize(\n        self,\n        value: Union[list[str], dict[str, str]],\n        attr: Union[str, None],\n        data: Union[Mapping[str, Any], None],\n        **kwargs: dict[str, Any],\n    ) -> list[str]:\n        if isinstance(value, list):\n            if all(isinstance(item, str) for item in value):\n                return value\n        elif isinstance(value, dict):\n            # Enforce list (for backward compatibility)\n            return []\n\n        raise ValidationError(\"Not a valid list\")\n\n\nclass DashboardJSONMetadataSchema(Schema):\n    # native_filter_configuration is for dashboard-native filters\n    native_filter_configuration = fields.List(fields.Dict(), allow_none=True)\n    # chart_configuration for now keeps data about cross-filter scoping for charts\n    chart_configuration = fields.Dict()\n    # global_chart_configuration keeps data about global cross-filter scoping\n    # for charts - can be overridden by chart_configuration for each chart\n    global_chart_configuration = fields.Dict()\n    chart_customization_config = fields.List(fields.Dict(), allow_none=True)\n    timed_refresh_immune_slices = fields.List(fields.Integer())\n    # deprecated wrt dashboard-native filters\n    filter_scopes = fields.Dict()\n    expanded_slices = fields.Dict()\n    expand_all_slices = fields.Boolean()\n    refresh_frequency = fields.Integer()\n    # deprecated wrt dashboard-native filters","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/dashboards/schemas.py#L177-L213","documentation":"SharedLabelsColorsField deserializes the shared-labels color config: it accepts a list of strings (the current format), returns [] when given a dict (legacy backward compatibility), and rejects everything else with 'Not a valid list'. It is a custom marshmallow field on the dashboard schema.","triggerScenarios":"PUT/POST /api/v1/dashboard/ with shared_labels_colors set to a non-list/non-dict value: a bare string, a number, null in a disallowed position, or a list containing non-string items (e.g. [1, 2]).","commonSituations":"API clients building the payload dynamically and passing an object where a list of color strings is expected; list items accidentally numbers; older payloads sending dicts (accepted, silently normalized to []).","solutions":["Send shared_labels_colors as an array of strings, e.g. [\"#00ff00\", \"#ff0000\"].","If you have a dict from an old export, either drop it (it becomes []) or convert its values to a list of strings yourself.","Map/validate values client-side: colors.every(c => typeof c === 'string')."],"exampleFix":"// before\nshared_labels_colors: { \"label1\": \"#00ff00\" }  // legacy dict, coerced to []\nshared_labels_colors: [1, 2]  // rejected\n\n// after\nshared_labels_colors: [\"#00ff00\", \"#0000ff\"]","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isSharedLabelsColors(v: unknown): v is string[] {\n  return Array.isArray(v) && v.every(x => typeof x === 'string');\n}","tryCatchPattern":null,"preventionTips":["Always send shared_labels_colors as string[]; convert dicts to [] or to their values.","Type the field in client models so non-string items fail at compile time."],"tags":["validation","dashboard","marshmallow","colors"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}