skip to content

When reporting a structured problem with Gradle's Problems API, what severity levels can you assign via the ProblemSpec, and what does each one mean for the build?

level: juniorimportance: must knowfreq 35%

answer

  1. Severity enum: ADVICE / WARNING / ERROR
  2. spec.severity(Severity.X)
  3. report() records, does NOT fail
  4. throwing() records AND fails
  5. severity is metadata, not control flow

basics

~20 s

Severity is set with spec.severity(...) using the Severity enum: WARNING, ERROR, and ADVICE. ERROR signals a real failure, WARNING a concern, and ADVICE a hint/suggestion. It classifies the problem; it does not by itself fail the build.

solid answer

~40 s

Gradle's Problems API lets a plugin attach a `Severity` to each reported problem through the `ProblemSpec.severity(Severity)` call. The enum has three members: `ADVICE` (a gentle hint, e.g. a deprecation-style nudge), `WARNING` (something the user should look at but the build can continue), and `ERROR` (a genuine fault). Severity is metadata for consumers — IDEs and the HTML problems report group and colour problems by it. Crucially, `severity(ERROR)` alone does **not** fail the build: reporting via `problems.getReporter().report(...)` only records the problem. To both record *and* fail you call `reporter.throwing(...)` with a `RuntimeException`, which emits the structured problem and then throws. So severity describes how serious the problem is, while the choice of `report` vs `throwing` decides whether the build stops.

code

kotlin · 5 lines
kotlin
reporter.report(problemId) { spec ->
    spec.severity(Severity.ERROR)
        .details("Plugin 'foo' requires Java 17+")
}
// NOTE: this records an ERROR-severity problem but the build still continues.

go deeper

for a junior

Name the three enum values and say severity classifies how serious the problem is.

for a middle

Add that report() records while throwing() fails the build, so ERROR alone doesn't stop anything.

for a senior

Explain why typed severity beats log-string scraping for IDE/Tooling-API consumers, and that it drives grouping in the HTML report.

for a principal

Frame severity as part of a stable diagnostic contract across console, HTML report, and Tooling API — pushing teams off ad-hoc logger.warn toward consumable diagnostics.

## What the Problems API is Gradle's **Problems API** (incubating, stabilising across Gradle 8.x) is a way for plugins and tasks to emit **structured diagnostics** instead of plain `logger.warn("...")` strings. A structured problem carries a category/id, a label, a severity, optional source locations, optional solutions, and a documentation link. Build tools and IDEs (via the Tooling API) consume these objects programmatically — so an IDE can show a clickable, categorised warning rather than scraping console text. You obtain the entry point by injecting `org.gradle.api.problems.Problems`, then `problems.getReporter()` gives a `ProblemReporter`. You describe a problem with a configuration lambda over a `ProblemSpec`. ## The Severity enum `org.gradle.api.problems.Severity` has exactly three constants: - **`ADVICE`** — a suggestion or hint. Lowest urgency; "you might prefer to do X". - **`WARNING`** — something is off but the build can still proceed. - **`ERROR`** — a genuine fault. You attach it with `spec.severity(Severity.WARNING)`. If you never call `severity(...)`, the problem still reports with a default. ## Severity does not control build failure This is the key gotcha. Reporting a problem records it; it never stops the build on its own — **even with `Severity.ERROR`**. There are two reporter methods: - `reporter.report(ProblemId, spec)` (or the id-via-spec overload) — record only, build continues. - `reporter.throwing(exception, ...)` — record the structured problem **and** throw the supplied exception, failing the build. So `ERROR` is a *classification*; `throwing` is the *action*. Treating `severity(ERROR)` as "this fails the build" is the classic mistake. ```kotlin val problems = project.serviceOf<Problems>() // or @Inject in a task/plugin val reporter = problems.reporter val id = ProblemId.create("missing-config", "Missing configuration", myGroup) reporter.report(id) { spec -> spec.severity(Severity.WARNING) .details("The 'apiKey' property was not set") } ``` ## Why structured severity matters to consumers The HTML **problems report** and IDE integrations bucket problems by severity, render colour/icons, and let users filter. Because the severity is a typed enum rather than a substring of a log line, consumers stay robust as wording changes.

  • If severity(ERROR) doesn't fail the build, how do you both report a structured problem and stop the build?
    Use reporter.throwing(...) passing a RuntimeException — it emits the structured problem and then throws, failing the build. report(...) only records.
  • What is the default severity if you never call severity()?
    The problem still gets a default severity (WARNING in current Gradle), but you should set it explicitly to be unambiguous for consumers.

saying these in an interview costs you the question

  • Claiming Severity.ERROR automatically fails the build — it does not; report() only records.
  • Inventing extra levels like INFO/FATAL/CRITICAL — there are exactly three: ADVICE, WARNING, ERROR.

context