nodejs/node · error · Exception
Unexpected error while reading %s: %s
Error message
Unexpected error while reading %s: %s
What it means
Thrown by LoadPythonDictionary in gypi_to_gn.py when Python's eval() of the .gypi file's text raises any exception other than SyntaxError (SyntaxError is re-raised separately with the filename attached). The eval runs with __builtins__ set to None, so the file must be a pure literal dict. The message embeds the offending file path and the str() of the original exception, so the underlying cause is preserved in the message text.
Source
Thrown at tools/gypi_to_gn.py:228
def TranslateToGnChars(s):
for code in s.encode('utf-8'):
if code in (34, 36, 92): # For '"', '$', or '\\'.
yield '\\' + chr(code)
elif 32 <= code < 127:
yield chr(code)
else:
yield '$0x%02X' % code
def LoadPythonDictionary(path):
file_string = open(path).read()
try:
file_data = eval(file_string, {'__builtins__': None}, None)
except SyntaxError as e:
e.filename = path
raise
except Exception as e:
raise Exception("Unexpected error while reading %s: %s" % (path, str(e)))
assert isinstance(file_data, dict), "%s does not eval to a dictionary" % path
# Flatten any variables to the top level.
if 'variables' in file_data:
file_data.update(file_data['variables'])
del file_data['variables']
# Strip all elements that this script can't process.
elements_to_strip = [
'conditions',
'direct_dependent_settings',
'target_conditions',
'target_defaults',
'targets',
'includes',
'actions',
]View on GitHub (pinned to 1b2de5e052)
Solutions
- Read the parenthesized cause in the message (the second %s) — it is str(original_exception), so it names the exact problem and often the line.
- Open the .gypi file and confirm it is a single Python literal dictionary: keys are strings, values are literals, no function calls or bare name references, no use of disabled builtins.
- If the file is generated, regenerate it from its gyp source rather than editing it by hand.
- If you must call LoadPythonDictionary programmatically, pre-validate the file with ast.literal_eval in a scratch check first.
Example fix
// before (offending .gypi contents)
{
'variables': {
'list': sorted(['b','a']), # NameError-like: call not allowed in sandbox
},
}
// after
{
'variables': {
'list': ['a','b'],
},
} Defensive patterns
Strategy: validation
Validate before calling
import ast
def is_safe_gypi(path):
src = open(path).read()
try:
ast.literal_eval(src) # rejects calls / names
return True
except (ValueError, SyntaxError):
return False Try / catch
try:
data = LoadPythonDictionary(path)
except Exception as e:
# message is 'Unexpected error while reading <path>: <cause>'
cause = str(e).rsplit(': ', 1)[-1]
raise SystemExit('bad .gypi %s: %s' % (path, cause)) Prevention
- Keep .gypi files as pure literal dicts — no function calls or name references.
- Treat .gypi as generated artifacts; regenerate from gyp rather than editing by hand.
- Pre-validate with ast.literal_eval before invoking the build step.
When it happens
Trigger: Invoking `gypi_to_gn.py <file>` (or calling LoadPythonDictionary directly) where <file> contains a Python expression that eval rejects at evaluation time rather than parse time: referencing an undefined name (NameError), calling a function, accessing a builtin like len/range/True-as-builtin (builtins are disabled), arithmetic that fails (ZeroDivisionError), or any object whose repr/eval round-trips badly.
Common situations: A .gypi file was hand-edited and now references a variable or calls a function; the file references True/False/None through a name that resolves to something unexpected under the sandboxed eval; a stale or corrupt .gypi produced by a buggy gyp generation step; pasting content that includes a trailing expression or macro call.
Related errors
- --depfile requires --stamp and vice versa
- --depfile requires --stamp and vice versa
- Could not find pattern matching %s
- GYP_MSVS_OVERRIDE_PATH requires GYP_MSVS_VERSION to be set t
- %s: error: no build_file
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/3bbc3e7779f5a7f3.
Report an issue: GitHub.