## TL;DR

Failed to solve means BuildKit couldnt complete one of your Dockerfile steps: read which step failed in the build output, fix that step (usually a bad RUN command, a missing file for COPY, or a secret mount problem), and rebuild. The error names the failing step if you look past the summary line.

## The error

```text
ERROR: failed to solve: process "/bin/sh -c npm ci" did not complete successfully: exit code: 1
```

Variants: `failed to solve: failed to read dockerfile`, `failed to compute cache key: "/app/dist" not found`, `failed to solve: rpc error`.

## Use this when

- `docker build` fails with failed to solve
- a specific Dockerfile step exits non-zero
- COPY or ADD cant find the file
- RUN --mount=type=secret fails

## Not for

- `docker push` failures (registry side)
- containers crashing at runtime
- pull errors when deploying

## Steps

1. Find the failing step. Rebuild with plain progress to see it clearly:

```bash
docker build --progress=plain -t myapp:test . 2>&1 | grep -B 5 "ERROR"
```

Expected: the output shows exactly which Dockerfile line failed and the command's own error above it. The command's error is the real error; failed to solve is just the wrapper.

2. If a RUN step failed, reproduce it interactively:

```bash
docker build --target [earlier-stage] -t debug-stage .
docker run --rm -it debug-stage sh
```

Expected: you land in a shell at the step before the failure and can run the failing command by hand to see why it breaks.

3. Fix the common causes:

- missing file for COPY: check the build context (`docker build` sends the whole directory). Add a .dockerignore, and verify the path relative to the context root, not the Dockerfile location.
- apt/apk/yum failing: the base image moved or the package index is stale. Pin versions and run the update and install in one RUN layer.
- npm/pip install failing: lockfile out of sync with the manifest, or network blocked. Copy lockfiles first and install before copying source.
- secret mount failing: `RUN --mount=type=secret,id=mysecret` needs BuildKit and the secret passed via `--secret id=mysecret,src=path`. The src file must exist on the host.

4. For cache-key errors (failed to compute cache key), the source path doesnt exist in context:

```bash
ls -la [the-path-from-the-error]
```

Expected: the path is missing or misspelled. Fix the Dockerfile path or the .dockerignore that excluded it.

5. If the Dockerfile itself wont parse, validate the syntax:

```bash
docker build --check -t myapp:test .
```

Expected: reports the parse problem with a line number. Common: bad JSON array syntax in CMD/ENTRYPOINT, or a heredoc gone wrong.

6. Rebuild clean to confirm:

```bash
docker build --no-cache -t myapp:test .
```

Expected: build completes, image tagged. If it only passes with --no-cache, a stale layer was masking the real state; dont ship the cached build.

### Variant: failed to solve only in CI, works locally

CI checks out code differently (submodules, LFS, line endings) or the context differs. Print `ls` of the context in CI and diff against local.

### Variant: rpc error / connection reset during build

BuildKit daemon hiccup, not your Dockerfile. Restart the builder (`docker buildx rm` / recreate) and retry.

### Variant: multi-platform build fails on one arch

The failing step assumes the host arch (downloads an x86 binary on arm64). Gate arch-specific steps with TARGETARCH or use multi-arch base images.

## Why it happens

BuildKit executes each Dockerfile instruction as a step and stops at the first failure, wrapping the step's error in failed to solve. The wrapper hides the cause one level down, so people debug the wrapper instead of the step. Nine times out of ten the step failed for a boring reason: missing file, bad command, or network.

## Edge cases

- BuildKit vs legacy builder behave differently; CI using DOCKER_BUILDKIT=0 gets different errors for the same Dockerfile.
- Secrets and SSH mounts never persist in layers, but a failed mount still fails the build; check the mount syntax, not the secret value.
- Very large contexts slow every build and cause cache-key weirdness; .dockerignore is not optional.
- `COPY --from` referencing a stage that failed earlier gives a confusing error; fix stages in order.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_YVJaG9w1EJ0tAw7LF76b0A
