{"record":{"id":"b0853967df6ea460","repo":"nodejs/node","slug":"dictionary-key-is-not-a-valid-gn-identifier","errorCode":null,"errorMessage":"Dictionary key is not a valid GN identifier.","messagePattern":"Dictionary key is not a valid GN identifier\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/gypi_to_gn.py","lineNumber":151,"sourceCode":"      yield str(v)\n\n    elif isinstance(v, list):\n      yield '['\n      for i, item in enumerate(v):\n        if i > 0:\n          yield ','\n        for tok in GenerateTokens(item, level + 1):\n          yield tok\n      yield ']'\n\n    elif isinstance(v, dict):\n      if level > 0:\n        yield '{'\n      for key in sorted(v):\n        if not isinstance(key, str):\n          raise ValueError('Dictionary key is not a string.')\n        if not key or key[0].isdigit() or not key.replace('_', '').isalnum():\n          raise ValueError('Dictionary key is not a valid GN identifier.')\n        yield key  # No quotations.\n        yield '='\n        for tok in GenerateTokens(v[key], level + 1):\n          yield tok\n      if level > 0:\n        yield '}'\n\n    else:  # Not supporting float: Add only when needed.\n      raise ValueError('Unsupported type when printing to GN.')\n\n  can_start = lambda tok: tok and tok not in ',}]='\n  can_end = lambda tok: tok and tok not in ',{[='\n\n  # Adds whitespaces, trying to keep everything (except dicts) in 1 line.\n  def PlainGlue(gen):\n    prev_tok = None\n    for i, tok in enumerate(gen):\n      if i > 0:","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gypi_to_gn.py#L133-L169","documentation":"ValueError raised by GenerateTokens when a dict key is a string but not a valid GN identifier: it must be non-empty, must not start with a digit, and must contain only alphanumerics and underscores (key.replace('_','').isalnum()). This guard fires after the isinstance(key, str) check.","triggerScenarios":"A key like '1foo' (leading digit), 'foo-bar' (dash not allowed), '' (empty), or 'foo.bar' (dot not allowed). GN identifiers are [A-Za-z_][A-Za-z0-9_]*.","commonSituations":"GYP keys containing dashes/dots (common in GYP 'variables' or 'conditions' with hyphenated names) being converted to GN, where such characters are illegal in identifiers.","solutions":["Rename keys to use only alphanumerics and underscores before conversion.","Add a preprocessing step that maps problematic keys to valid GN identifiers.","Review GYP files for hyphenated/dotted keys that have no GN equivalent and refactor the build logic."],"exampleFix":"# before (GYP key 'include-dirs' is rejected)\n\n# after (rename to 'include_dirs' in GYP source / mapping)","handlingStrategy":"validation","validationCode":"def is_valid_gn_identifier(key: str) -> bool:\n    return bool(key) and not key[0].isdigit() and key.replace('_', '').isalnum()","typeGuard":"def valid_gn_keys(d: dict) -> bool:\n    return all(isinstance(k, str) and k and not k[0].isdigit()\n               and k.replace('_', '').isalnum() for k in d)","tryCatchPattern":"try:\n    list(GenerateTokens(data))\nexcept ValueError as e:\n    if 'valid GN identifier' in str(e):\n        pass  # rename keys (dashes->underscores) and retry","preventionTips":["Use only [A-Za-z_][A-Za-z0-9_]* keys in data destined for GN.","Preprocess GYP keys: replace '-' and '.' with '_'.","Maintain an explicit mapping for problematic GYP keys."],"tags":["gyp","gn","build","naming","identifiers"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}