{"record":{"id":"88df90db68ab970e","repo":"apache/superset","slug":"parse-error","errorCode":"parse_error","errorMessage":"err.message","messagePattern":"err\\.message","errorType":"error_code","errorClass":"EChartOptionsParseError","httpStatus":null,"severity":"error","filePath":"superset-frontend/plugins/plugin-chart-echarts/src/utils/safeEChartOptionsParser.ts","lineNumber":421,"sourceCode":"  }\n\n  const trimmed = input.trim();\n  if (!trimmed) {\n    return { success: true, data: undefined };\n  }\n\n  // Step 1: Parse into AST\n  const wrappedInput = `(${trimmed})`;\n  let ast: Node & { body: Array<Node & { expression: Node }> };\n\n  try {\n    ast = parse(wrappedInput, {\n      ecmaVersion: 2020,\n      sourceType: 'script',\n    }) as Node & { body: Array<Node & { expression: Node }> };\n  } catch (error) {\n    const err = error as Error & { loc?: { line: number; column: number } };\n    throw new EChartOptionsParseError(err.message, 'parse_error', [], err.loc);\n  }\n\n  if (\n    !ast.body ||\n    ast.body.length !== 1 ||\n    ast.body[0].type !== 'ExpressionStatement'\n  ) {\n    throw new EChartOptionsParseError(\n      'Input must be a single object literal expression (e.g., { key: value })',\n      'parse_error',\n    );\n  }\n\n  const { expression } = ast.body[0];\n\n  if (expression.type !== 'ObjectExpression') {\n    throw new EChartOptionsParseError(\n      `Expected an object literal, but got: ${expression.type}`,","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset-frontend/plugins/plugin-chart-echarts/src/utils/safeEChartOptionsParser.ts#L403-L439","documentation":"Raised in SlackV1Upgrade.update_recipients when json.loads(recipient.recipient_config_json) throws TypeError or ValueError for a Slack v1 recipient: the stored recipient_config_json column is not parseable JSON (or not a string at all). It aborts the v1→v2 Slack recipient migration before any writes, so recipients remain untouched.","triggerScenarios":"Executing a report with a Slack (v1) recipient whose recipient_config_json holds malformed JSON — hand-edited rows, legacy truncated data, or rows written by an old buggy client. json.loads raises ValueError (bad JSON) or TypeError (None/non-str).","commonSituations":"Direct DB edits to report_recipient rows; data migrated from a very old Superset version with a different config shape; JSON containing unescaped quotes truncated by column limits.","solutions":["Inspect the row: SELECT id, recipient_config_json FROM report_recipient WHERE id = <id>.","Repair the JSON to a valid object with a string 'target' field (or recreate the recipient via the UI).","Re-run the report; the migration will retry the upgrade."],"exampleFix":"-- before: invalid JSON stored\nUPDATE report_recipient SET recipient_config_json = '{\"target\":\"#general\"}' WHERE id = 7;\n-- (was: '{target:#general}' — unquoted keys)","handlingStrategy":"validation","validationCode":"import json\n\ndef valid_recipient_json(cfg: str) -> bool:\n    try:\n        json.loads(cfg)\n        return True\n    except (TypeError, ValueError):\n        return False","typeGuard":"def is_slack_v1_config(value: str) -> bool:\n    try:\n        parsed = json.loads(value)\n    except (TypeError, ValueError):\n        return False\n    return isinstance(parsed, dict) and isinstance(parsed.get(\"target\"), str)","tryCatchPattern":"try:\n    command.run()\nexcept NotificationParamException as ex:\n    if \"Invalid Slack recipient configuration\" in str(ex):\n        flag_recipient_row_for_repair(recipient_id)","preventionTips":["Always create Slack recipients through the Superset UI/API, never SQL.","Run a periodic JSON-validity check on report_recipient.recipient_config_json."],"tags":["slack","json","migration","recipients"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}