devantler-tech / devantler-tech/ksail

fix(image): the export content-repair path is silent, so every recurrence is undiagnosable

Open Beginner friendly
#6,804 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
165
Forks
12
Avg merge
5h 51m
Merged PRs (30d)
347

Description

> 🤖 Generated by the Agentic Engineer

## Evidence

`ksail workload export` failed again on 2026-08-31, blocking an unrelated Azure-SDK dependency bump
(#6798), on the `🧪 System Test (Docker) (Vanilla, Docker, true, --cni Calico --csi Disabled
--load-balancer Disabled)` leg:

```
► exporting images from cluster kind
✗ export images: ctr export failed for all images during individual export attempts
(initial bulk export error: container exec failed with exit code 1:
ctr: failed to get reader: content digest sha256:1d1ddb62... not found)
```

Same command, same `traefik/whoami:v1.10` image, and the same content-store cause as the recurring
signature already filed five times — #3964, #3986, #3987, #4091 ("Recurring Flake", same image),
#4491 — and root-caused in #6488.

## The problem: the repair path is completely silent, so no recurrence can be diagnosed

#6488 established that KSail already knows how to fix this: `tryExportImagesWithRepair` →
`refreshImageContent` re-pulls the image and re-exports. It fixed the *silent-truncation* form.

This failure is the **other** form — the non-zero exit that the repair was already gated on. So the
repair should have engaged. **I could not determine from a 532 KB CI log whether it did**, and that
is the actual defect being reported here.

In `pkg/svc/image/repair.go`, three different outcomes produce **byte-identical** operator-visible
output:

| what happened | what the log shows |
|---|---|
| repair never attempted (`len(repairImages) == 0`) | the original export error |
| repair attempted, `refreshImageContent` failed → `return images, exportErr` | the original export error |
| repair attempted, re-export failed | the original export error |

`refreshImageContent` and `refreshSingleImageContent` emit **no output at all**. The only diagnostic
in the file is `logFailedImageExports`, which is unreachable here: it is called from
`fallbackExportImages` only when `len(successfulImages) > 0`, and this failure had **zero**
successful images.

The result is that the one piece of information needed to tell "recoverable flake" from "the repair
is broken" is never recorded. That is the mechanical reason this signature "keeps being closed as
transient" — the phrase in #6488's own title — and why each recurrence costs a fresh investigation
that cannot reach a verdict. It has now consumed six issues.

## Impact

- **Operators:** a user hitting this gets an error that does not say a self-repair was attempted or
why it failed, so the actionable next step is invisible.
- **CI:** unrelated PRs go red and are re-run blind. Re-running is currently the only available
response, which is precisely how a genuine regression would be papered over.

## Expected behaviour

The repair path states what it did. Enough signal to distinguish the three rows above, without
making a successful repair noisy.

## Acceptance criteria

- [ ] When `isMissingContentError` matches and repair is attempted, that is recorded, naming the
images being refreshed.
- [ ] When `refreshImageContent` fails, its error is surfaced alongside the original export error
rather than discarded — today `return images, exportErr` drops `refreshErr` entirely.
- [ ] When repair is **skipped** because `repairImages` is empty, that is stated, since it is a
silent no-op on a whole-cluster export by design.
- [ ] A test pins that the repair-attempted and repair-failed paths are distinguishable.
- [ ] A successful repair stays quiet enough not to add noise to green runs.

**Size:** small. This is diagnostics and error-wrapping in one file; it deliberately does **not**
change the repair logic, which #6488 already fixed.

**Note:** this does not claim the export flake itself is unfixed. It claims the next recurrence
should be diagnosable, which today it is not — I hit exactly that wall investigating this one.

Contributor guide

Open the contributing guide

Research direction

Start in pkg/svc/image/repair.go, reading tryExportImagesWithRepair, refreshImageContent, refreshSingleImageContent, and logFailedImageExports. Trace the missing-content and empty-repairImages paths, then add a test distinguishing repair attempted from repair failed. Done means repair failures retain their errors, skipped repairs are recorded, and successful repairs remain quiet.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, go
Domain
cli, devops
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.