nodejs/node · error · GypError

Incompatible list policies

Error message

Incompatible list policies {k} and {list_incompatible}

What it means

GYP list keys support merge-policy suffixes: base (append), 'base=' (replace), 'base?' (only-if-absent), 'base+' (prepend). Certain combinations are meaningless (replace+append, replace+prepend, etc.). When a suffixed key and an incompatible sibling are both present in the same 'fro' dict, gyp raises GypError listing both policies.

Solutions

  1. Keep at most one merge policy per list base within a single dict (append and prepend are the only pair that may coexist).
  2. Remove the redundant/contradictory suffixed key.
  3. If you need replace semantics, use only 'key=' and drop the bare 'key'.

Example fix

// before: { 'sources': [...], 'sources=': [...] }
// after: { 'sources=': [...] }  // replace only
Defensive patterns

Strategy: validation

Validate before calling

import re
base_keys = {}
for k in the_dict:
    m = re.match(r'^(.*?)([=?+/!]?)$', k)
    base_keys.setdefault(m.group(1), set()).add(k)
for base, keys in base_keys.items():
    if len(keys) > 1 and not keys <= {base, base+'+'}:
        raise SystemExit('Incompatible list policies: %r' % keys)

Try / catch

try:
    gyp.main(args)
except gyp.input.GypError as e:
    if 'Incompatible list policies' in str(e): print('Remove one of the conflicting suffixed keys'); raise

Prevention

When it happens

Trigger: Within one dict being merged, two of {list_base, list_base+'=', list_base+'?', list_base+'+'} that the code marks incompatible coexist. E.g. both 'sources' and 'sources=', or 'sources?' and 'sources+', etc.

Common situations: Copy-pasting sources lists with different suffixes; an include adding 'sources+' while the target already has 'sources='; refactoring that left stale suffixed keys; misunderstanding the suffix semantics.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/f1bea76c9f6145c2. Report an issue: GitHub.

Appendix: source

Thrown at tools/gyp/pylib/gyp/input.py:2344

                lists_incompatible = [list_base, list_base + "?"]
                to[list_base] = []
            elif ext == "+":
                list_base = k[:-1]
                lists_incompatible = [list_base + "=", list_base + "?"]
                append = False
            elif ext == "?":
                list_base = k[:-1]
                lists_incompatible = [list_base, list_base + "=", list_base + "+"]
            else:
                list_base = k
                lists_incompatible = [list_base + "=", list_base + "?"]

            # Some combinations of merge policies appearing together are meaningless.
            # It's stupid to replace and append simultaneously, for example.  Append
            # and prepend are the only policies that can coexist.
            for list_incompatible in lists_incompatible:
                if list_incompatible in fro:
                    raise GypError(
                        "Incompatible list policies " + k + " and " + list_incompatible
                    )

            if list_base in to:
                if ext == "?":
                    # If the key ends in "?", the list will only be merged if it doesn't
                    # already exist.
                    continue
                elif not isinstance(to[list_base], list):
                    # This may not have been checked above if merging in a list with an
                    # extension character.
                    raise TypeError(
                        "Attempt to merge dict value of type "
                        + v.__class__.__name__
                        + " into incompatible type "
                        + to[list_base].__class__.__name__
                        + " for key "
                        + list_base

View on GitHub (pinned to 1b2de5e052)