# Docker buildx bake failed in CI

> A docker buildx bake failed message comes from one of four layers. Learn to tell an HCL diagnostic from a target lookup, and what bake ignores.

Source: https://latchkey.dev/learn/docker/docker-buildx-bake-failed-in-ci  
Updated: 2026-09-21

A docker buildx bake failed run is refused by one of four layers, and they word things so differently that the shape of the message tells you where to look before you read it. An HCL diagnostic carries a file, a line and a column range; bake's own errors are a plain sentence; and one class of mistake produces no message at all.

## What this error means

A bake invocation exits non zero without building, or builds the wrong thing. The useful distinction is punctuation: a message with a filename, a comma separated line and column range and a semicolon is the HCL parser, a bare sentence naming a target is bake, and a familiar Dockerfile or solve error is BuildKit after bake finished its own work. The block below is three bake runs on this machine.

```Captured locally on buildx v0.35.0-desktop.2, 2026-09-21
--- a misspelled target name
ERROR: failed to find target relase
--- a variable that is not defined, refused by the HCL parser
ERROR: var.hcl:2,23-30: Unknown variable; There is no variable named "MISSING"., and 1 other diagnostic(s)
--- a misspelled attribute inside a target, which is not refused at all
(exit 0, and the printed target shows the default context and dockerfile)
```

## Common causes

### The target name does not match anything in the file

A typo, a target that was renamed, or a group that no longer contains what it used to. Because matching is by pattern rather than equality, a name that looks close is not close: either the pattern matches or nothing does. This is the most common bake failure in our experience and the cheapest to confirm with the print flag.

### A variable is referenced and never defined

HCL refuses a variable it has no definition for, with a diagnostic naming the line and the column range of the reference. The fix is a default in a variable block, which also documents the value for the next person. A variable defined only by an environment variable in one workflow is the usual way this reaches CI, because it works everywhere the environment is set.

### bake could not decide what kind of file it was given

bake accepts HCL, JSON and Compose files, and when a file parses as none of them the error carries both attempts: the YAML failure and the HCL one in a single sentence. Reading only the first half sends you to fix a YAML file that was never meant to be YAML.

### An attribute name inside a target is wrong

This is the silent case. The attribute is dropped and the target keeps the default for it, so a build runs with the wrong context or the wrong Dockerfile and nothing is reported. It surfaces as a strange image rather than as a failure, which is why it is worth a print step in CI rather than trust.

### Everything resolved and the build itself failed

Once bake has produced its targets, the errors are ordinary build errors and none of them are bake specific. If your message names a Dockerfile line, a stage or a cache key, the bake layer did its job and the problem is downstream. The pages linked below cover the common ones.

## How to fix it

### Print the resolved configuration and compare it with what you meant

1. Run the print flag with the same targets and the same file arguments the failing job used.
2. Read the context, dockerfile and tags for each target rather than scanning for errors.
3. A default where you expected your own value is a silently dropped attribute, not a bake bug.

```.github/workflows/build.yml
- name: Show what bake resolved
  run: docker buildx bake --print ${{ matrix.target }}

- name: Build
  run: docker buildx bake ${{ matrix.target }}
```

### Give every variable a default in the file

A default turns an environment dependent failure into a value you can read, and it keeps the bake file runnable on a laptop. Where a value genuinely must come from the environment, a default that is obviously a placeholder still beats a diagnostic about a name HCL has never seen.

```docker-bake.hcl
# docker-bake.hcl
variable "TAG" { default = "dev" }
variable "REGISTRY" { default = "ghcr.io/acme" }

target "release" {
  context    = "."
  dockerfile = "Dockerfile"
  tags       = ["${REGISTRY}/api:${TAG}"]
}
```

### Read a two part parse error as two attempts, not one failure

When bake cannot tell what the file is, it reports the Compose failure and the HCL failure together. Decide which format you meant, then read only that half. Naming the file with an extension bake recognizes removes the ambiguity for the next run.

```Terminal
# name files so bake does not have to guess
docker buildx bake -f docker-bake.hcl release
docker buildx bake -f compose.yaml release
```

### Keep the target list in the file rather than in the workflow

A group in the bake file is checked by the print step above; a list of target names typed into a workflow matrix is not checked by anything until it runs. Moving the list into a group means a rename breaks in one place and is caught by the print step.

```docker-bake.hcl
# docker-bake.hcl
group "ci" {
  targets = ["api", "web", "worker"]
}
```

## How to prevent it

- Run the print flag in CI before the build, and treat an unexpected default as a failure.
- Give every bake variable a default so a missing environment value cannot stop a build.
- Keep target lists in groups inside the bake file rather than in workflow matrices.
- Name bake files with an extension that makes the format unambiguous.

## Identify the layer from the punctuation

bake is a thin coordinator over three other things: a configuration language, a target resolver of its own, and then the ordinary build. Each has its own error style, and once you can tell them apart you know which file to open.

The HCL style is the most distinctive. A diagnostic renders as the filename, a colon, the line, a comma, the start and end columns, then a colon, a summary, a semicolon and a detail sentence. When more than one diagnostic is produced, only the first is shown and the count of the rest is appended, which is why real bake failures often end with a phrase about other diagnostics. That whole line is assembled from three separate fields of a diagnostic structure and exists as a literal nowhere.

| Layer that refused | Shape of what it prints | File to open |
| --- | --- | --- |
| The HCL parser | file:line,col-col: Summary; Detail., and N other diagnostic(s) | Your bake file, at that line and column |
| bake target resolution | A bare sentence naming a target or a key | Your bake file, or the command line |
| The Compose loader, for a YAML bake file | A validation sentence with a dotted path | The compose file bake was handed |
| BuildKit, after bake resolved everything | The usual solve and Dockerfile errors | The Dockerfile the target names |

> Read in docker/buildx bake/bake.go and its vendored hashicorp/hcl v2 diagnostic type on 2026-09-21, with the three cases run locally.

## Target names are glob patterns, not literals

bake matches the names you pass on the command line against its groups and targets with path matching, not with equality. `docker buildx bake api-*` is a supported way to build several targets, and it is also why a name with a stray bracket or asterisk behaves in ways a literal comparison would not.

When nothing matches you get a bare sentence naming the pattern. There are three of these sentences in bake with slightly different wording and quoting, depending on which resolution path you took, so do not read too much into the exact punctuation: what they all mean is that nothing in the file matched.

```Terminal
# builds every target whose name starts with api-
docker buildx bake api-*

# and this is what a name that matches nothing looks like
# ERROR: failed to find target relase
```

## The failure mode with no message

We put a misspelled attribute inside a target block, ran bake with the print flag, and it exited zero and printed a target carrying the default context and the default Dockerfile. Nothing warned that the attribute had been ignored. This is the worst case in the whole family, because the build then runs against a configuration you did not write and produces an image that is wrong rather than a job that is red.

It also means the print flag is not only a debugging convenience. It is the only thing that shows you what bake decided, and comparing its output against what you meant is the only check that catches a silently dropped attribute.

```Terminal
# always print before you build, in CI as well as locally
docker buildx bake --print release
docker buildx bake release
```

> The print flag resolves variables, matches targets and inherits, then writes the effective configuration as JSON without building anything.

## Why no recorded run backs this page

Everything on this page happens before a builder is contacted. Two of the three captured cases exit before any network call at all, and the third exits after reading a local file. A recorded Latchkey run would be a job that spent its minutes on a print flag.

The claim worth taking care over is which layer produces which shape, and we checked that by reading the diagnostic type that formats the line and column form, the three bake sentences for an unmatched target, and then running all three cases to see the output. The silent case in particular is the sort of thing a recorded run would hide rather than show, because a green job with a wrong image looks like a success.

## FAQ

### What does failed to find target mean in buildx bake?

Nothing in the file matched the name you passed. Matching is by pattern rather than by equality, so a close spelling is not close at all. Run the print flag with no target to list what the file actually defines, then compare.

### Why does my bake error have a line and a column range?

Because it came from the HCL parser rather than from bake. A diagnostic renders as the file, the line, the column range, a summary, a semicolon and a detail. When several are produced only the first is shown and the remaining count is appended, so the phrase about other diagnostics is normal.

### Will bake tell me if I misspell an attribute name?

No. We put a misspelled attribute in a target block and bake exited zero, printing the target with the default value for that attribute. Nothing is warned about. The print flag is the only thing that will show you, which is why it belongs in CI rather than only in debugging.

### Can I build more than one target in a single bake call?

Yes, and it is the normal way to use it. Pass several names, pass a pattern that matches several, or define a group and pass the group. Because names are matched as patterns, a group and a pattern behave similarly, and a group is easier to review.

## References

- [buildx: bake target resolution and its error sentences](https://github.com/docker/buildx/blob/master/bake/bake.go)
- [HCL: the diagnostic type that formats file, line and column](https://github.com/hashicorp/hcl/blob/main/diagnostic.go)
- [Docker docs: bake file reference](https://docs.docker.com/build/bake/reference/)
- [Docker docs: docker buildx bake](https://docs.docker.com/reference/cli/docker/buildx/bake/)

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
