{"record":{"id":"ba603193a0349b3e","repo":"goharbor/harbor","slug":"chart-name-is-illegal","errorCode":null,"errorMessage":"chart name: {} is illegal","messagePattern":"chart name: (.+?) is illegal","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"tools/migrate_chart/migrate_chart.py","lineNumber":78,"sourceCode":"                version = chart_data.get('version')\n                name = chart_data.get('name')\n                return name, version\n            else:\n                raise Exception(\"Failed to read chart.yaml from the chart tgz file. filename {}\".format(chart_tgz_path))\n        else:\n            raise Exception(\"chart.yaml not found in the chart tgz file. filename {}\".format(chart_tgz_path))\n\nclass ChartV2:\n\n    def __init__(self, filepath:Path):\n        self.filepath = filepath\n        self.project = self.filepath.parts[-2]\n        self.name = \"\"\n        self.version = \"\"\n        try:\n            self.name, self.version = read_chart_version(filepath)\n            if self.name == \"\" or self.version == \"\" or self.name is None or self.version is None :\n                raise Exception('chart name: {} is illegal'.format('-'.join(parts)))\n        except Exception as e:\n            click.echo(\"Skipped chart: {} due to illegal chart name. Error: {}\".format(filepath, e), err=True)\n        return\n\n    def __check_exist(self, hostname, username, password):\n        return requests.get(CHART_URL_PATTERN.format(\n                host=hostname,\n                project=self.project,\n                name=self.name,\n                version=self.version),\n                auth=requests.auth.HTTPBasicAuth(username, password))\n\n    def migrate(self, hostname, username, password):\n        res = self.__check_exist(hostname, username, password)\n        if res.status_code == 200:\n            raise Exception(\"Artifact already exist in harbor\")\n        if res.status_code == 401:\n            raise Exception(res.reason)","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/goharbor/harbor/blob/7b2fd08cc568955cca339afeefab27372840d936/tools/migrate_chart/migrate_chart.py#L60-L96","documentation":"Raised inside ChartV2.__init__ when read_chart_version succeeded but the parsed Chart.yaml produced an empty or None name or version. Note the latent bug: the message formats '-'.join(parts) but `parts` is not defined in scope, so what actually raises is a NameError; the surrounding except swallows it, prints 'Skipped chart ... illegal chart name', and the chart is later skipped in the migration loop for having empty name/version.","triggerScenarios":"Chart.yaml parses as YAML but lacks a `name:` or `version:` key, has empty values, or has case typos like `Version:` — read_chart_version returns None/empty for the missing field and this branch fires.","commonSituations":"Hand-edited Chart.yaml files, template-generated charts where the version was never bumped, apiVersion v2 charts with omitted fields.","solutions":["Open the chart's Chart.yaml and add non-empty `name` and `version` fields","Validate before placing the tgz in chart storage: helm lint or helm show chart <tgz>","Fix the format-string bug so the real diagnostics print instead of a NameError","Re-run the migration; skipped charts stay unmigrated until corrected"],"exampleFix":"# before\nraise Exception('chart name: {} is illegal'.format('-'.join(parts)))  # NameError: 'parts' undefined\n# after\nraise Exception('chart name/version is illegal: {} (name={!r}, version={!r})'.format(filepath, self.name, self.version))","handlingStrategy":"validation","validationCode":"import tarfile, yaml\n\ndef chart_metadata_ok(tgz_path) -> bool:\n    with tarfile.open(tgz_path, 'r:gz') as tar:\n        for m in tar.getmembers():\n            if m.isfile() and m.name.endswith('Chart.yaml'):\n                data = yaml.safe_load(tar.extractfile(m)) or {}\n                name, version = data.get('name'), data.get('version')\n                return bool(name) and bool(version) and isinstance(name, str) and isinstance(version, str)\n    return False","typeGuard":null,"tryCatchPattern":"try:\n    chart = ChartV2(path)\nexcept Exception as e:\n    # ChartV2 already swallows this internally; guard the outer loop anyway\n    click.echo(f'Skipped chart {path}: {e}', err=True)\n    continue","preventionTips":["Run helm lint on charts before packaging","Assert name and version are non-empty strings in your chart CI template","Fix the '-'.join(parts) NameError in the tool so real diagnostics are visible"],"tags":["python","helm","chart-migration","yaml","harbor"],"backgroundTag":null,"analyzedSha":"7b2fd08cc568955cca339afeefab27372840d936","analyzedAt":"2026-08-16T00:00:10.961Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}