# How to Troubleshoot Jenkins Build Failures: A Deep Dive into the Core Execution Model

> Quickly troubleshoot Jenkins build failures. Learn to diagnose issues by inspecting logs, build actions, and workspace artifacts using the core execution model.

- Repository: [Jenkins/jenkins](https://github.com/jenkinsci/jenkins)
- Tags: deep-dive
- Published: 2026-07-29

---

**Jenkins build failures are determined by the `Result` enum in [`hudson.model.Result.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson.model.Result.java), which is calculated and persisted by the `Run` class and can be diagnosed by inspecting console logs, build actions, and workspace artifacts programmatically or via the REST API.**

Troubleshooting Jenkins build failures requires understanding how the **jenkinsci/jenkins** source code tracks execution state. The platform models every build as a `Run` object that aggregates console output, build actions, and a final `Result` status. By tracing how these components interact in [`core/src/main/java/hudson/model/Run.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Run.java) and related files, you can pinpoint exactly why a build marked itself as `FAILURE`, `UNSTABLE`, or `ABORTED`.

## The Core Execution Model: Run, AbstractBuild, and Result

At the heart of every Jenkins build is the `hudson.model.Run` class. This abstract base class, defined in [`core/src/main/java/hudson/model/Run.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Run.java), represents any single execution of a job, tracking start time, duration, and the immutable `Result`. For traditional Freestyle jobs, `hudson.model.AbstractBuild` extends `Run` and adds workspace management and fingerprint handling in [`core/src/main/java/hudson/model/AbstractBuild.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/AbstractBuild.java).

The outcome of any build is encoded by the `hudson.model.Result` enum in [`core/src/main/java/hudson/model/Result.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Result.java). This enum defines five possible states: `SUCCESS`, `UNSTABLE`, `FAILURE`, `ABORTED`, and `NOT_BUILT`. The class provides utility methods such as `isWorseThan()` and `combine()` that Jenkins uses to determine the final status when multiple stages or post-build actions contribute to the result.

## How Jenkins Calculates the Final Build Status

When a build completes, the `Run.doFinish(Result result)` method finalizes the state. If no result is provided (e.g., the build was aborted early), Jenkins defaults to `Result.ABORTED`. The method then merges the new result with any existing status using `Result.combine()`, which follows a strict precedence order: `FAILURE` > `UNSTABLE` > `ABORTED` > `NOT_BUILT` > `SUCCESS`.

This means a single failed step can override a `SUCCESS`, but an `UNSTABLE` result (typically from test failures) will not mask a `FAILURE`. The final value is persisted to disk, and `RunListener` extensions are notified via `RunListener.fireOnCompleted()`, allowing external systems to react to the definitive status stored in [`core/src/main/java/hudson/model/RunListener.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/RunListener.java).

## Diagnostic Entry Points: Console Logs and Build Actions

The console log provides the primary diagnostic surface. Jenkins streams build output to a text file on the controller, accessible via `Run.getLogFile()` and exposed in the UI through `Run.getConsole()`. Plugins can filter this stream by extending `hudson.console.ConsoleLogFilter` (referenced in [`core/src/main/java/hudson/util/ConsoleLogFilter.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/util/ConsoleLogFilter.java)) to mask secrets or annotate lines.

Beyond the console, build actions offer structured failure data. Any object implementing `hudson.model.Action` (defined in [`core/src/main/java/hudson/model/Action.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Action.java)) can be attached to a run to expose test results, coverage metrics, or static analysis warnings. You can enumerate these programmatically using `Run.getActions()` to discover why a build was marked `UNSTABLE` despite appearing successful in the console.

## Common Failure Patterns and Root Causes

| Symptom | Typical Cause | Source Location |
|---------|---------------|-----------------|
| **`BUILD FAILURE`** with non-zero exit code | A shell step returned an exit code ≠ 0, translated to `Result.FAILURE` in `Run.doFinish()`. | [`core/src/main/java/hudson/model/Run.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Run.java) |
| **`UNSTABLE`** with test failures | The `JUnitResultArchiver` creates a `JUnitResultAction` that evaluates `isFailed()` and sets the result via `Result.UNSTABLE`. | [`core/src/main/java/hudson/model/Result.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Result.java) |
| **`ABORTED`** after user cancellation | The `Run.doStop()` method forcibly sets the result to `Result.ABORTED` when a user clicks the stop button. | [`core/src/main/java/hudson/model/Run.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Run.java) |
| Missing artifacts | The `ArtifactArchiver` only preserves files when `Run.getResult().isBetterOrEqualTo(Result.SUCCESS)`, causing apparent "missing" files on failed builds. | Artifact archiver logic in `core/src/main/java/hudson/model/` hierarchy |
| Plugin stack traces | Exceptions thrown during `Run.doRun()` are caught, logged to the Jenkins system log, but often hidden from the build console, leaving the result as `FAILURE` without clear user-facing output. | [`core/src/main/java/hudson/model/Run.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Run.java) |

## Programmatic Troubleshooting Techniques

You can diagnose failures without web UI access by querying the Jenkins model or REST API.

### 1. Inspect the Console Log of the Last Build

Run this Groovy script in the **Script Console** (`Manage Jenkins > Script Console`) to retrieve the full log and result of any job:

```groovy
def job = Jenkins.instance.getItemByFullName('folder/example-job')
def lastRun = job.getLastBuild()
println "Result: ${lastRun.getResult()}"
println "=== Console Log ==="
lastRun.getLog().each { line -> println line }

```

This script invokes `Run.getResult()` defined in [`core/src/main/java/hudson/model/Run.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Run.java) and reads the log file via `Run.getLog()`.

### 2. Query Build Status via REST API

Use the Jenkins JSON API from any external system to check the result without parsing HTML:

```bash
JOB="example-job"
BASE="http://jenkins.example.com"
curl -s "${BASE}/job/${JOB}/lastBuild/api/json" | jq -r '.result'

```

The API returns the string representation of the `Result` enum (e.g., `FAILURE`, `SUCCESS`), directly reflecting the internal state set by `Run.doFinish()`.

### 3. Handle Unstable Results in Pipeline

If your tests fail but you want the pipeline to continue, the `junit` step automatically marks the run `UNSTABLE`:

```groovy
pipeline {
    agent any
    stages {
        stage('Test') {
            steps {
                sh 'mvn test || true'
                junit '**/target/surefire-reports/*.xml'
            }
        }
    }
    post {
        unstable {
            echo 'Build is unstable due to test failures.'
        }
    }
}

```

The `junit` step internally creates a `JUnitResultAction` and calls `Run.setResult(Result.UNSTABLE)` if any tests fail, leveraging the logic in [`core/src/main/java/hudson/model/Result.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Result.java).

### 4. Enumerate Build Actions for Debugging

When artifacts or reports seem missing, verify which actions are attached to the run:

```groovy
def run = currentBuild.rawBuild
run.getActions().each { action ->
    println "Action: ${action.class.name} - ${action.displayName}"
}

```

This accesses the `Action` list defined in [`core/src/main/java/hudson/model/Action.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Action.java) and exposed through `Run.getActions()`.

## Summary

- **Build outcomes** are encapsulated by the `Result` enum in [`core/src/main/java/hudson/model/Result.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Result.java), with `FAILURE` overriding all other statuses according to the precedence in `combine()`.
- The `Run` class in [`core/src/main/java/hudson/model/Run.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Run.java) persists timestamps, logs, and the final result, calculated by `doFinish()` and compared via `isWorseThan()`.
- **Console logs** are accessed via `Run.getLog()` and filtered through `ConsoleLogFilter`, while **build actions** provide structured data via `Run.getActions()`.
- **Programmatic diagnosis** is possible through Groovy scripts accessing the Jenkins object model or via the REST API returning JSON representations of the `Result`.
- Common failures map to specific source behaviors: non-zero exit codes trigger `FAILURE`, test archivers trigger `UNSTABLE`, and user cancellations invoke `doStop()` to set `ABORTED`.

## Frequently Asked Questions

### What is the difference between FAILURE and UNSTABLE in Jenkins?

`FAILURE` indicates the build did not complete successfully, typically because a shell step returned a non-zero exit code or an exception was thrown during `Run.doRun()`. `UNSTABLE` means the build completed but one or more post-build actions, such as the `JUnitResultArchiver`, detected warnings or test failures. The [`Result.java`](https://github.com/jenkinsci/jenkins/blob/main/Result.java) enum defines `FAILURE` as worse than `UNSTABLE`, so if any step fails, the final result will be `FAILURE` regardless of unstable conditions.

### How do I access the console log of a specific build programmatically?

You can retrieve the console log by obtaining the `Run` object for a build and calling `getLog()`, which returns a `List<String>` of log lines. From the Jenkins Script Console, use `Jenkins.instance.getItemByFullName('job-name').getBuildByNumber(123).getLog()`. For external access, use the URL `/job/JOBNAME/BUILD_NUMBER/consoleText`, which streams the raw log file stored at `$JENKINS_HOME/jobs/JOBNAME/builds/BUILD_NUMBER/log`.

### Why does my pipeline show UNSTABLE even though the shell command exit code was 0?

A zero exit code only guarantees the **step** succeeded; subsequent post-build steps like `junit`, `checkstyle`, or `findbugs` scan the workspace for result files and call `Run.setResult(Result.UNSTABLE)` if they detect issues. Because `Result.combine()` preserves the worst status encountered, even a successful shell command cannot override the `UNSTABLE` state set by a test result publisher.

### Where are build results stored on the Jenkins controller?

Each build result is persisted to an XML file named [`build.xml`](https://github.com/jenkinsci/jenkins/blob/main/build.xml) inside the build directory, typically located at `$JENKINS_HOME/jobs/JOB_NAME/builds/BUILD_NUMBER/build.xml`. This file contains the serialized `Run` object, including the `Result` ordinal and timestamp. The console log is stored as a plain text file named `log` in the same directory, allowing administrators to inspect failures directly on the filesystem when the UI is inaccessible.