What went wrong?
SAVE ARTIFACT … AS LOCAL inside a FINALLY block writes to the wrong directory when the TRY body fails. On success it resolves relative to the Earthfile's directory; on failure it falls back to the project/run root.
Inherited from upstream earthly#3531 (still open there); confirmed it reproduces in EarthBuild.
Minimal repro — test/Earthfile:
VERSION --try 0.8
pass:
FROM alpine
TRY
RUN touch foo
FINALLY
SAVE ARTIFACT foo AS LOCAL ./foo
END
fail:
FROM alpine
TRY
RUN touch foo && false
FINALLY
SAVE ARTIFACT foo AS LOCAL ./foo
END
Run from the parent dir:
| target |
RUN exit |
foo lands at |
correct? |
+pass |
0 |
test/foo (Earthfile dir) |
yes |
+fail |
1 |
foo (run root) |
no |
What should have happened?
AS LOCAL should resolve to the same path regardless of TRY-body success/failure — i.e. test/foo in both cases. Exporting-on-failure is the whole point of FINALLY; landing the file somewhere else defeats it.
Real-world impact: a TRY/FINALLY audit target (e.g. npm audit → SARIF, then exit non-zero on high vulns) in a sub-Earthfile saving to scan_reports/x.sarif exports to the repo root on failure, so a CI upload step guarded by hashFiles('scan_reports/*.sarif') finds nothing — the report is missing in exactly the failing case it exists for.
Workaround: make the target always succeed, write the verdict to a sidecar file artifact, and fail the CI step outside Earthly based on it.
Related upstream TRY/FINALLY-on-failure bugs worth porting/tracking: earthly#2452 (large files truncated), earthly#2817 (directories rejected), earthly#4177 (no WITH DOCKER).
What earthly version?
earth version dev-69dff834-cachemiss 69dff834 darwin/arm64; macOS 26.5.1
(local dev build of the fork). Body limits also seen: TRY accepts a single command only, and VERSION --try is required.
Other Helpful Information
is a directory / truncation variants tracked upstream as above; this issue is scoped to the wrong-directory-on-failure case (earthly#3531).
What went wrong?
SAVE ARTIFACT … AS LOCALinside aFINALLYblock writes to the wrong directory when theTRYbody fails. On success it resolves relative to the Earthfile's directory; on failure it falls back to the project/run root.Inherited from upstream earthly#3531 (still open there); confirmed it reproduces in EarthBuild.
Minimal repro —
test/Earthfile:Run from the parent dir:
RUNexitfoolands at+passtest/foo(Earthfile dir)+failfoo(run root)What should have happened?
AS LOCALshould resolve to the same path regardless ofTRY-body success/failure — i.e.test/fooin both cases. Exporting-on-failure is the whole point ofFINALLY; landing the file somewhere else defeats it.Real-world impact: a
TRY/FINALLYaudit target (e.g.npm audit→ SARIF, then exit non-zero on high vulns) in a sub-Earthfile saving toscan_reports/x.sarifexports to the repo root on failure, so a CI upload step guarded byhashFiles('scan_reports/*.sarif')finds nothing — the report is missing in exactly the failing case it exists for.Workaround: make the target always succeed, write the verdict to a sidecar file artifact, and fail the CI step outside Earthly based on it.
Related upstream
TRY/FINALLY-on-failure bugs worth porting/tracking: earthly#2452 (large files truncated), earthly#2817 (directories rejected), earthly#4177 (noWITH DOCKER).What earthly version?
(local dev build of the fork). Body limits also seen:
TRYaccepts a single command only, andVERSION --tryis required.Other Helpful Information
is a directory/ truncation variants tracked upstream as above; this issue is scoped to the wrong-directory-on-failure case (earthly#3531).