nodejs/node · error · GypError
Xcode environment variables are cyclically dependent: ${node
Error message
Xcode environment variables are cyclically dependent: ${nodes} What it means
Raised by _TopologicallySortedEnvVarKeys when Xcode environment variables form a reference cycle. The function builds a dependency graph of environment variables that reference each other via ${VAR} interpolation and attempts a topological sort; a CycleError from gyp.common is caught and re-raised as a GypError naming the offending nodes. This means two or more xcode_settings variables reference each other in a loop.
Source
Thrown at tools/gyp/pylib/gyp/xcode_emulation.py:1877
# Use a definition of edges such that user_of_variable -> used_variable.
# This happens to be easier in this case, since a variable's
# definition contains all variables it references in a single string.
# We can then reverse the result of the topological sort at the end.
# Since: reverse(topsort(DAG)) = topsort(reverse_edges(DAG))
matches = {v for v in regex.findall(env[node]) if v in env}
for dependee in matches:
assert "${" not in dependee, "Nested variables not supported: " + dependee
return matches
try:
# Topologically sort, and then reverse, because we used an edge definition
# that's inverted from the expected result of this function (see comment
# above).
order = gyp.common.TopologicallySorted(env.keys(), GetEdges)
order.reverse()
return order
except gyp.common.CycleError as e:
raise GypError(
"Xcode environment variables are cyclically dependent: " + str(e.nodes)
)
def GetSortedXcodeEnv(
xcode_settings, built_products_dir, srcroot, configuration, additional_settings=None
):
env = _GetXcodeEnv(
xcode_settings, built_products_dir, srcroot, configuration, additional_settings
)
return [(key, env[key]) for key in _TopologicallySortedEnvVarKeys(env)]
def GetSpecPostbuildCommands(spec, quiet=False):
"""Returns the list of postbuilds explicitly defined on |spec|, in a form
executable by a shell."""
postbuilds = []
for postbuild in spec.get("postbuilds", []):View on GitHub (pinned to 1b2de5e052)
Solutions
- Examine the cycle nodes reported in the error message (the 'nodes' list) and find the variables that reference each other.
- Break the cycle by removing or rewriting one of the ${VAR} references so the dependency graph becomes acyclic.
- Replace circular references with literal values or computed values set once.
- Use only forward references: ensure each variable's ${...} expansions point to already-defined or built-in variables.
Example fix
# before
'xcode_settings': {
'FOO': '${BAR}',
'BAR': '${FOO}',
}
# after
'xcode_settings': {
'FOO': '/abs/path',
'BAR': '${FOO}/sub',
} Defensive patterns
Strategy: validation
Validate before calling
# Detect cyclic variable references before gyp generation
def find_cycles(env):
import re
graph = {k: set(re.findall(r'\$\{(\w+)\}', v)) for k, v in env.items()}
visited, stack = set(), []
def dfs(node):
if node in stack:
return stack[stack.index(node):]
if node in visited:
return None
visited.add(node); stack.append(node)
for dep in graph.get(node, []):
c = dfs(dep)
if c: return c
stack.pop()
return None
for n in graph:
c = dfs(n)
if c: return c
return None Try / catch
from gyp.common import GypError
try:
sorted_keys = _TopologicallySortedEnvVarKeys(env)
except GypError as e:
if 'cyclically dependent' in str(e):
print('fix circular xcode_settings variable references:', e)
raise Prevention
- Audit xcode_settings for ${VAR} references and ensure they form a DAG.
- Avoid bidirectional variable references like A=${B} and B=${A}.
- Prefer computed literals over chained variable interpolation.
When it happens
Trigger: An xcode_settings (or inherited environment) dict defines variables like A=${B} and B=${A}, or a longer chain A=${B}, B=${C}, C=${A}. TopologicallySorted detects the cycle and the nodes involved are reported.
Common situations: Hand-written gyp config with cross-referencing environment variables. Copy-pasting xcode_settings from another project that introduced an indirect circular reference. A variable accidentally set to reference itself.
Related errors
- stabs debug format is not supported yet.
- Unknown debug format %s
- Multiple toolsets not supported in xcode build (target %s)
- xcodebuild returned unexpected results
- No Xcode or CLT version detected!
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/2ba82477d609c708.
Report an issue: GitHub.