[BUG] Cache gateway masks failures in conditional shell callers #27

Open
opened 2026-09-12 15:42:03 +02:00 by erikinkinen · 0 comments
Owner

Summary

The executable cache gateway can return success after a preparation, setup or
copy command fails when its shell function is called inside a conditional.
Other publication failures lose their original status or leave temporary or
invalid cache entries. This can misreport a failed host build operation.

Reproduction

Use the original scripts/lib/service-build.sh from Integration commit
27ca5c035c569b11cf44260ca7c2fb587082e17e with an isolated component, cache,
dependency record and original byte-producing command fixture. Call
build_cached_dynamic_artifact inside if ...; then ...; else ...; fi.
Inject one selected local utility failure while allowing its ordinary fixture
operation to finish, or provide a helper that prints a digest and exits nonzero.
The maintained CacheFailureTests regressions exercise these cases without
compiling Rust or booting a VM. Run them using:

python3 -W error tests/dynlink_cache_selection_test.py CacheFailureTests

The retained original-gateway replay fails deterministically; the corrected
gateway preserves the injected statuses and rejects incomplete publication.

Expected and actual behavior

Expected: return the first failure, stop before dependent work, close owned
descriptors and remove unpublished temporary entries where cleanup succeeds.
Report any secondary cleanup failure without replacing the first error.

Observed with the original gateway: dependency-identity status 41, fingerprint
status 3, setup statuses 31/32/37, and a cache-hit copy status 33 became exit 0
with output present. Hash, directory and move failures instead became status 1;
the failed move retained a temporary complete entry. Real checksum/marker write
errors returned 1 while leaving an invalid published entry. A failed cache read
also triggered a second producer invocation. Conditional preparation changed a
partial-digest failure 45 to success and a dependency failure 46 to status 1.

Environment and identity

  • Repository/source: Integration
    27ca5c035c569b11cf44260ca7c2fb587082e17e, original gateway bytes; the same
    source is retained independently of later unrelated signed checkpoints.
  • Configuration: isolated host fixture inputs, explicit tool fingerprint and
    private cache/output roots. The byte producer is an original test program.
  • Execution: x86-64 Linux host, Bash 5.3.9, Python 3.14.7 and util-linux flock
    2.41.5; no compiler, image, firmware or VM participates in the reproduction.
  • Input route: explicitly selected source, linker, dependency identity and
    command arguments; failure injection is confined to the fixture shell.
  • Last known working revision: not established.

Evidence and prior checks

Original-source replays retain exact commands, exit statuses and bounded logs.
The gateway replay has seven test methods with thirteen failed assertions;
the preparation replay has one method with two failed assertions. These are
separate from the selected-input aliasing defect. The final focused host cache
suites pass 32 tests with Python warnings denied under a 120-second hard,
30-second output-stall and 8-MiB output owner. No image or guest-build evidence
is claimed.

Published correction: signed Integration 381bccd87c5632e9d1af8aa9d0aca295cbc00e1f in WIP PR #12. The final 32 warning-denied cache tests and existing shell fixture pass; every original fixture assertion is preserved. CI syntax and all 113 Markdown files pass. Original failing replays remain retained. Current-head CI, complete dependency closure and fresh image/guest acceptance remain open.

Tracking

Related selected-input cache correction:
#24.
Acceptance requires meaningful failure/cleanup regressions and continued
observation of the existing host cache fixture in CI.

## Summary The executable cache gateway can return success after a preparation, setup or copy command fails when its shell function is called inside a conditional. Other publication failures lose their original status or leave temporary or invalid cache entries. This can misreport a failed host build operation. ## Reproduction Use the original `scripts/lib/service-build.sh` from Integration commit `27ca5c035c569b11cf44260ca7c2fb587082e17e` with an isolated component, cache, dependency record and original byte-producing command fixture. Call `build_cached_dynamic_artifact` inside `if ...; then ...; else ...; fi`. Inject one selected local utility failure while allowing its ordinary fixture operation to finish, or provide a helper that prints a digest and exits nonzero. The maintained `CacheFailureTests` regressions exercise these cases without compiling Rust or booting a VM. Run them using: ```sh python3 -W error tests/dynlink_cache_selection_test.py CacheFailureTests ``` The retained original-gateway replay fails deterministically; the corrected gateway preserves the injected statuses and rejects incomplete publication. ## Expected and actual behavior Expected: return the first failure, stop before dependent work, close owned descriptors and remove unpublished temporary entries where cleanup succeeds. Report any secondary cleanup failure without replacing the first error. Observed with the original gateway: dependency-identity status 41, fingerprint status 3, setup statuses 31/32/37, and a cache-hit copy status 33 became exit 0 with output present. Hash, directory and move failures instead became status 1; the failed move retained a temporary complete entry. Real checksum/marker write errors returned 1 while leaving an invalid published entry. A failed cache read also triggered a second producer invocation. Conditional preparation changed a partial-digest failure 45 to success and a dependency failure 46 to status 1. ## Environment and identity - Repository/source: Integration `27ca5c035c569b11cf44260ca7c2fb587082e17e`, original gateway bytes; the same source is retained independently of later unrelated signed checkpoints. - Configuration: isolated host fixture inputs, explicit tool fingerprint and private cache/output roots. The byte producer is an original test program. - Execution: x86-64 Linux host, Bash 5.3.9, Python 3.14.7 and util-linux flock 2.41.5; no compiler, image, firmware or VM participates in the reproduction. - Input route: explicitly selected source, linker, dependency identity and command arguments; failure injection is confined to the fixture shell. - Last known working revision: not established. ## Evidence and prior checks Original-source replays retain exact commands, exit statuses and bounded logs. The gateway replay has seven test methods with thirteen failed assertions; the preparation replay has one method with two failed assertions. These are separate from the selected-input aliasing defect. The final focused host cache suites pass 32 tests with Python warnings denied under a 120-second hard, 30-second output-stall and 8-MiB output owner. No image or guest-build evidence is claimed. Published correction: signed Integration `381bccd87c5632e9d1af8aa9d0aca295cbc00e1f` in [WIP PR #12](https://git.erikinkinen.fi/erix/integration/pulls/12). The final 32 warning-denied cache tests and existing shell fixture pass; every original fixture assertion is preserved. CI syntax and all 113 Markdown files pass. Original failing replays remain retained. Current-head CI, complete dependency closure and fresh image/guest acceptance remain open. ## Tracking Related selected-input cache correction: https://git.erikinkinen.fi/erix/integration/issues/24. Acceptance requires meaningful failure/cleanup regressions and continued observation of the existing host cache fixture in CI.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
erix/integration#27
No description provided.