skip to content

What do `outgoingVariants` and `resolvableConfigurations` report, and when would you use each?

level: seniorimportance: should knowfreq 38%

answer

  1. outgoingVariants = producer/consumable side
  2. resolvableConfigurations = consumer/resolvable side
  3. attributes + capabilities + artifacts
  4. debug 'no matching variant' / ambiguous
  5. consumable vs resolvable vs bucket roles

basics

~20 s

outgoingVariants lists what your project publishes/exposes to consumers — the consumable configurations, their attributes, and artifacts. resolvableConfigurations lists what your project consumes — the resolvable configurations and the attributes they request. Together they explain variant-aware resolution.

solid answer

~40 s

Gradle's dependency resolution is *variant-aware*: producers expose **consumable** configurations (variants) tagged with **attributes** (e.g. `org.gradle.usage=java-api`, `org.gradle.jvm.version=17`), and consumers use **resolvable** configurations that request matching attributes. `outgoingVariants` reports the producer side: every consumable variant, its attributes, capabilities, and the artifacts it serves — exactly what a downstream project or `maven-publish` will see. `resolvableConfigurations` reports the consumer side: each resolvable configuration and the attributes it asks for. You reach for these when you hit a *variant selection* error — "no matching variant" or "ambiguous variants" — because the failure message and these two reports together let you compare requested vs. offered attributes and find the mismatch. They're the diagnostic counterpart to the attribute-matching engine.

code

bash · 7 lines
bash
# Producer: what variants/attributes does this project expose?
gradle outgoingVariants --variant runtimeElements

# Consumer: what attributes does this configuration request?
gradle resolvableConfigurations --configuration compileClasspath

# Compare the attribute sets to find a 'no matching variant' mismatch.

go deeper

for a junior

Aware that Gradle matches variants by attributes and that these tasks exist; deep detail not expected.

for a middle

Run them to see attributes/artifacts and recognize producer vs consumer sides.

for a senior

Diff requested vs offered attributes to root-cause variant selection failures and fix configuration roles/attributes.

for a principal

Design attribute schemas and consumable variants for published libraries so downstream resolution is unambiguous across the org.

## Variant-aware resolution in one paragraph Modern Gradle doesn't just pick a jar — it matches **variants**. A *producer* exposes one or more **consumable** configurations (e.g. `apiElements`, `runtimeElements`), each carrying **attributes** (key→value metadata like `org.gradle.usage`, `org.gradle.category`, `org.gradle.jvm.version`, `org.gradle.libraryelements`) and optionally **capabilities**. A *consumer* declares a **resolvable** configuration (e.g. `compileClasspath`) that *requests* attributes. Gradle selects the producer variant whose attributes are compatible with the requested ones. Configurations have roles: `consumable` (`canBeConsumed=true`), `resolvable` (`canBeResolved=true`), or plain *buckets* (neither — like `implementation`). ## `outgoingVariants` — the producer view ```bash gradle outgoingVariants gradle outgoingVariants --variant runtimeElements # focus one ``` For each consumable variant it prints: - **Attributes** (what consumers will match against), - **Capabilities** (default = `group:name:version`), - **Artifacts** (the files served, e.g. the jar), - any **secondary variants** (e.g. classes/resources directories). This is what publication and downstream consumers see, so it's the report to check when a consumer can't select your project. ## `resolvableConfigurations` — the consumer view ```bash gradle resolvableConfigurations gradle resolvableConfigurations --configuration testRuntimeClasspath ``` For each resolvable configuration it prints the **requested attributes** and the **extended-from** hierarchy, i.e. exactly what the consumer side asks for. ## Putting them together When resolution fails with `No matching variant` or `The consumer was configured to find … but: …`, you: 1. Run `resolvableConfigurations` to see what the consuming configuration *requested*. 2. Run `outgoingVariants` on the producer to see what attributes it *offered*. 3. Diff the attribute sets — a mismatch (e.g. consumer wants `jvm.version=17`, producer only offers `21`) is the culprit. ## Why two tasks Resolution is two-sided. Splitting producer (`outgoingVariants`) from consumer (`resolvableConfigurations`) mirrors the model and makes the diff obvious.

  • How do these reports relate to configuration roles (`canBeConsumed` / `canBeResolved`)?
    `outgoingVariants` lists consumable configurations (`canBeConsumed=true`); `resolvableConfigurations` lists resolvable ones (`canBeResolved=true`). Plain dependency buckets like `implementation` are neither and appear in neither report.
  • You get 'No matching variant of project :lib was found'. Walk through the diagnosis.
    Run `resolvableConfigurations` on the consumer to see requested attributes, `outgoingVariants` on `:lib` to see offered attributes, then diff them — the failing attribute (e.g. usage, jvm.version, libraryelements) is the mismatch to fix via attribute config or a matching variant.

outgoingVariants is a vending machine's labeled product slots (what's on offer + specs); resolvableConfigurations is the buyer's order with required specs. A mismatch means nothing dispenses.

saying these in an interview costs you the question

  • Treating attributes as cosmetic — they drive selection.
  • Confusing which task is producer vs consumer side.
  • Assuming `implementation` shows up in these reports (it's a non-resolvable, non-consumable bucket).

context