{"record":{"id":"bfe771cb12367cae","repo":"apache/superset","slug":"display-name-must-contain-at-least-one-letter-or-n","errorCode":null,"errorMessage":"Display name must contain at least one letter or number","messagePattern":"Display name must contain at least one letter or number","errorType":"exception","errorClass":"ExtensionNameError","httpStatus":null,"severity":"error","filePath":"superset-extensions-cli/src/superset_extensions_cli/utils.py","lineNumber":283,"sourceCode":"        Cleaned display name\n\n    Raises:\n        ExtensionNameError: If display name is invalid\n    \"\"\"\n    if not display_name or not display_name.strip():\n        raise ExtensionNameError(\"Display name cannot be empty\")\n\n    # Normalize whitespace: strip and collapse multiple spaces\n    normalized = \" \".join(display_name.strip().split())\n\n    if not DISPLAY_NAME_REGEX.match(normalized):\n        raise ExtensionNameError(\n            \"Display name must start with a letter and can contain letters, numbers, spaces, hyphens, underscores, and dots (e.g., 'Dashboard Widgets')\"\n        )\n\n    # Check for only whitespace/special chars after normalization\n    if not any(c.isalnum() for c in normalized):\n        raise ExtensionNameError(\n            \"Display name must contain at least one letter or number\"\n        )\n\n    return normalized\n\n\ndef suggest_technical_name(display_name: str) -> str:\n    \"\"\"\n    Suggest technical name from display name.\n\n    Args:\n        display_name: Human-readable name (e.g., \"Dashboard Widgets!\")\n\n    Returns:\n        Technical name suggestion (e.g., \"dashboard-widgets\")\n    \"\"\"\n    # Normalize for identifiers\n    normalized = _normalize_for_identifiers(display_name)","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset-extensions-cli/src/superset_extensions_cli/utils.py#L265-L301","documentation":"Thrown by validate_display_name() as a defensive backstop: after normalization, the name must contain at least one alphanumeric character. In practice this branch is nearly unreachable because DISPLAY_NAME_REGEX already requires the first character to be a letter — it exists to guarantee the invariant even if the regex is ever relaxed. Seeing it means the normalized name passed the regex yet had no letters/digits, which implies a regex/normalization mismatch.","triggerScenarios":"A direct call to validate_display_name() with input that normalizes to only special characters (e.g. '...', ' - ', '___') — normally the regex check at line 277 fires first, so hitting this exact line requires the regex to have been changed or a Unicode edge case where characters match the regex but are not str.isalnum().","commonSituations":"Custom forks that loosened DISPLAY_NAME_REGEX; display names built from Unicode letter-like characters that behave differently between the regex (ASCII-oriented pattern) and str.isalnum(); rarely seen in stock usage.","solutions":["Include at least one ASCII letter or digit in the display name (e.g. 'Dash ...' -> 'Dash Widgets').","If you maintain a fork and relaxed DISPLAY_NAME_REGEX, keep this alnum guard consistent or update it too.","Avoid display names composed solely of punctuation/symbols."],"exampleFix":"# before\nvalidate_display_name(\"...\")\n\n# after\nvalidate_display_name(\"Dots Extension ...\")","handlingStrategy":"validation","validationCode":"normalized = \" \".join(display_name.strip().split())\nif not any(c.isalnum() for c in normalized):\n    raise ValueError(\"display name needs at least one letter or digit\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always call validate_display_name() before suggest_technical_name() so impossible names are rejected earlier with a clearer message."],"tags":["cli","validation","defensive","extensions"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}