--- name: rez-resolve-troubleshooting description: "Diagnosing Rez resolve failures — a command-by-command triage path for 'The context failed to resolve', package conflicts, request version conflicts, implicit package failures, variant selection failures, missing packages or versions, filtered or ignored packages, and stale resolve caches. Use when rez-env, rez-build or rez-test fails to resolve and you need to find the culprit, not just read the solver. Covers Rez 3.4.0." --- # Rez resolve failure troubleshooting > **One-sentence summary**: capture the failure without entering a shell, read the failure path that > rez already printed, classify it into one of six causes, then confirm with the cheapest command > that can rule a cause in or out. Skill scope: **diagnosing a failure that already happened**. For how the solver works and how to read `-v` traces see `rez-resolve`; for request and version syntax see `rez-core-concepts`; for the command surface see `rez-cli`. ## Step 0 — capture the failure Never start by debugging inside a subshell; `rez-env` drops you into an interactive shell on success and hides the structured output on failure. Write the context to a file instead: ```bash rez-env -o context.rxt # also stores a FAILED resolve rez-env -o - # or write it to stdout ``` The stored `.rxt` keeps `failure_description`, so you can re-inspect it later with `rez-context`: ```bash rez-context context.rxt --so # what was requested, and the source order rez-context context.rxt --print-graph # dot graph of the failed resolve ``` Two traps in that pair, both verified against `src/rez/cli/context.py`: - **`-i` is `--interpret` on `rez-context`, not `--input`** (it *is* `--input` on `rez-env`, so the same short flag means opposite things on the two commands). Passing `-i` sends the call down the interpret branch, where `--so` and the graph flags are skipped, and a failed context raises `ResolvedContextError: Cannot perform operation in a failed context` — the one case this skill exists for. - **`--dependency-graph` needs a solved context**, because it calls `get_dependency_graph()`. Use `--print-graph` (or `--pg`), which calls `rc.graph()` and works on a failed resolve. On a failed context it prints the `CONFLICT` edge directly: ```text digraph g { _1 [fillcolor="#F6F6F6", fontsize="10", style="filled,dashed", label="python-2"]; _2 [fillcolor="#F6F6F6", fontsize="10", style="filled,dashed", label="!python-2"]; _1 -> _2 [arrowsize="1", color="red", fontcolor="red", style="bold", label="CONFLICT"]; } ``` Of the other graph flags: `-d` / `--dependency-graph` also fails, because it calls `get_dependency_graph()`, which is decorated `@_on_success`. But `-g` / `--graph` is fine — it uses `rc.graph()`, the same undecorated call as `--print-graph`, so it does render the failure graph. It just needs Graphviz `dot` on `PATH`, and fails with `FileNotFoundError` when it is missing; that is an environment problem, not a failed-context one. ## Step 1 — read what rez already told you A failed resolve prints three blocks, in this order. They are the whole diagnosis; everything after this step only confirms it. ``` The context failed to resolve: The following package conflicts occurred: (python-2.7 <--!--> python-3) Resolve paths starting from initial requests to conflict: my_tool --> my_lib-1.2 --> python-3 To see a graph of the failed resolution, add --fail-graph in your rez-env or rez-build command. ``` | Block | Tells you | |---|---| | `The context failed to resolve:` + reason | the failure class (see the table below) | | `Resolve paths starting from initial requests to …` | **who** pulled in the offending requirement: it reads left (your request) to right (the conflict), so read it backwards to find the culprit | | `--fail-graph` hint | the visual version of the same path | The reason line comes from `failure_description`; the path lines come from `rez.utils.resolve_graph.failure_detail_from_graph`, which finds the `CYCLE` edge first and the `CONFLICT` edge second. `<--!-->` marks two requirements that cannot both hold. In `(A <--!--> B)`, `A` is the dependency that a package's variants share and `B` is the request it conflicts with — so `B` is usually the thing to change. A leading `.` on a name marks an **ephemeral** package, which is resolved but not actually installed into the environment. If the paths block is empty, the failure is a total reduction (all variants of a scope were removed) rather than a conflict — go to cause 5. ## Step 2 — classify and confirm Work through the causes cheapest-first. Each has one command that confirms it. | # | Cause | Signature | Confirm with | |---|---|---|---| | 1 | **Request version conflict** | `(python-2.7 <--!--> python-3)` — both sides the same package | `rez-depends ` | | 2 | **Package conflict** | `(maya-2019 <--!--> !maya-2019)` — different packages, or an explicit `!` | `rez-depends A`, `rez-depends B` | | 3 | **Implicit package** | conflict mentions `platform`, `arch`, `os`; or a package vanishes only on this machine | `rez-env --ni` | | 4 | **Variant selection** | wrong variant chosen, or variant subpath missing | `rez-config variant_select_mode`, `rez-env -v` | | 5 | **Total reduction / no candidates** | no conflict edge; `The context failed to resolve` with no named pair | `rez-search `, `rez-env --no-filters` | | 6 | **Package or version not found** | `PackageFamilyNotFoundError` / `PackageNotFoundError` | `rez-search `, `rez-config packages_path` | Also rule out a **stale cache** before anything else — see the last section. ### Cause 1 — request version conflict Two requirements on the same package cannot both hold. Rez reports the two ranges. ```bash rez-env my_tool my_lib -o /tmp/c.rxt # (python-2.7 <--!--> python-3) rez-depends python # reverse lookup: who depends on python ``` The package you asked for is rarely the culprit. The paths block names the chain from your request down to the conflict; `rez-depends ` is the reverse lookup that confirms it from the other direction — it lists the packages that depend on ``. Then: - **Loosen the tighter request.** `foo-1.2+<1.3` may be stricter than reality requires — a range like `foo-1.2+<2` often resolves. Check what actually exists with `rez-search foo`. - **Use a weak reference** when you want "if present, it must be in this range" rather than a hard requirement: `rez-env foo '~nuke-9.rc2'`. This is how DCC packages constrain an embedded python without forcing it into every environment. - **Use the conflict operator** to exclude a specific bad version: `rez-env maya_utils '!maya-2015.6'`. - **Bisect**: drop requests one at a time until it resolves, then add back to find the pair. ### Cause 2 — package conflict Two *different* packages are mutually exclusive, usually because a package declared `!other`. ```bash rez-depends A # reverse lookup: who depends on A rez-env --max-fails 5 # collect several failures, not just the first rez-env --fail-graph # render the resolve graph image ``` `--max-fails N` matters when the first failure is a symptom: resolving it can expose a second, unrelated conflict that is the real one. The default is `-1` (report everything). ### Cause 3 — implicit packages Rez adds `implicit_packages` to **every** request. The defaults are weak requirements: ```python implicit_packages = [ "~platform=={system.platform}", "~arch=={system.arch}", "~os=={system.os}", ] ``` Because they are weak (`~`), they do not force those packages into the resolve; they constrain them **only if present**. A platform-dependent package that is present therefore has to match the current machine, and that is usually the real cause when a resolve fails on one OS but not another. ```bash rez-env --ni # retry with no implicit packages at all rez-config implicit_packages # what is actually configured ``` - **`--ni` makes it resolve** → an implicit package is the constraint. Look for a package whose variants or `requires` name `platform`, `arch` or `os`, and check it was built for this machine. - **`--ni` still fails** → the conflict is in your own request; go back to causes 1 and 2. - Do not "fix" this by editing `implicit_packages` globally. It is the mechanism that stops a Linux-built variant from being selected on Windows; removing it trades a clear failure for a silently wrong environment. ### Cause 4 — variant selection failure Two distinct failures look similar, so separate them first: **Wrong variant chosen.** When variants are not mutually exclusive, which one you get is a *preference*, not a guarantee. `version_priority` (the default) prefers variants whose packages have higher versions, giving priority to packages that appear in the request, then to packages listed earlier in the variant. `intersection_priority` instead prefers the variant with the most packages present in the request, with version as a tiebreak. ```bash rez-config variant_select_mode # which mode is active ``` Rez cannot compare versions across different packages, so with variants like `["maya-2016"]` and `["houdini-14"]` the choice is effectively arbitrary unless the discriminator is in the request. Fix it by **naming the discriminator in the request** — `rez-env geocache maya` — not by reordering variants and hoping. Remember a transitive dependency can still overrule the preference. **Wrong or missing variant requirement.** Variants are appended to `requires` per variant, so a variant that omits a requirement you expect will not constrain anything: ```bash rez-config error_on_missing_variant_requires # default True rez-env -v # see which variant scope survived ``` **Useful extra checks** when the resolved variant is not the one you built: ```bash rez-env --nl # ignore ~/packages — a local build may be shadowing rez-env --no-local # long form of the same flag ``` A locally installed package sits at the front of `packages_path` and will shadow the released one, including shadowing a Windows resolve with a Linux-only local build. ### Cause 5 — total reduction, and nothing found at all If every variant of a scope is removed there is no conflict edge to print, so the paths block is empty. The candidates were eliminated one by one. Find out what *does* exist: ```bash rez-search # every version rez can see rez-search --latest # just the newest rez-search -f '{qualified_name}' # just the names, one per line rez-search 'foo<2' # narrow by a real version range rez-env --no-filters # retry with package filters disabled rez-env --exclude '*.beta' # reproduce a filter locally rez-env --paths # retry against a specific search path ``` Three `rez-search` rules that matter here, all verified against a real repository: - **A bare name already lists every version.** With the default `--type auto`, `rez-search ` resolves to a single family and lists all of its versions: ```text $ rez-search foo foo-1.0.0 foo-1.1.0 foo-2.0.0 ``` You do **not** need to add a version range to list versions. - **`-*` is not a version range, and matches nothing.** `foo-*` is not a valid requirement, so `package_search.py` falls back to treating the whole string as a glob on the *family name*, and `fnmatch('foo', 'foo-*')` is false. It returns `No matching family found` (exit 1) on every repository, which looks like "the package doesn't exist" but is the pattern never matching. | To do this | Write | |---|---| | list every version of one package | `rez-search foo` | | narrow to a range | `rez-search 'foo-1'` / `rez-search 'foo<2'` | | list versions across several families | `rez-search 'foo*' --type package` | A glob that matches **one** family still degrades correctly to versions (`rez-search 'f*'` lists the `foo` versions), but a glob matching **two or more** families degrades to a family list (`rez-search '*'` prints `bar` and `foo`). Force versions with `--type package`. - **`-f` / `--format` takes an argument.** It is `-f FORMAT`, not a boolean flag; `rez-search --format` exits 2 with `expected one argument`. It only changes the output template. `--latest` works with a bare name: `rez-search foo --latest` prints `foo-2.0.0`. Package filters hide packages from resolves: a package is excluded when it matches an exclusion rule and no inclusion rule. A filter can be set globally via the `package_filter` setting, applied per command with `--exclude` / `--include`, or attached to a package with `rez-pkg-ignore`. Because a filtered package is simply invisible, the symptom is "it exists on disk but rez won't use it". ```bash rez-config package_filter # global rules rez-search --no-local # check the released repo, not your local install ``` Also check timestamps: `rez-env -t