> ## Documentation Index
> Fetch the complete documentation index at: https://tally.wharflab.com/llms.txt
> Use this file to discover all available pages before exploring further.

# tally/copy-from-empty-scratch-stage

> Detects COPY --from referencing a scratch stage with no file-producing instructions.

Detects COPY --from referencing a scratch stage with no file-producing instructions.

| Property | Value       |
| -------- | ----------- |
| Severity | Error       |
| Category | Correctness |
| Default  | Enabled     |

## Description

Detects `COPY --from=<stage>` instructions where the source stage is `FROM scratch` and contains
no `ADD`, `COPY`, or `RUN` instructions. Since scratch stages start with an empty filesystem,
any `COPY --from` referencing such a stage is guaranteed to fail at build time.

Common causes:

* A stage was renamed or deleted during a refactor, leaving an empty placeholder
* An AI patch accidentally removed the instructions that populated the stage
* A `COPY`/`RUN` was moved to a different stage but the `COPY --from` reference wasn't updated

Instructions like `ENV`, `LABEL`, `EXPOSE`, `WORKDIR`, and `USER` do not produce filesystem
content and are not considered file-producing for this check.

## Examples

### Bad

```dockerfile theme={null}
# "artifacts" stage is scratch with no file-producing instructions
FROM scratch AS artifacts

FROM alpine:3.19
COPY --from=artifacts /out/app /usr/local/bin/app
```

### Good

```dockerfile theme={null}
FROM golang:1.22 AS builder
RUN go build -o /out/app ./...

# "artifacts" stage has a COPY that populates it
FROM scratch AS artifacts
COPY --from=builder /out/app /out/app

FROM alpine:3.19
COPY --from=artifacts /out/app /usr/local/bin/app
```

## Related rules

* [`tally/shell-run-in-scratch`](./shell-run-in-scratch) — a scratch stage with only a shell-form
  `RUN` is not considered empty by this rule (any `RUN` counts as file-producing). The
  `shell-run-in-scratch` rule warns about the failing `RUN` instead. If the user removes that `RUN`,
  this rule will then fire on downstream `COPY --from` references.

## Configuration

```toml theme={null}
[rules.tally.copy-from-empty-scratch-stage]
severity = "error"  # Options: "off", "error", "warning", "info", "style"
```
