A Per-Row Failure Report for Bulk Image Alt-Text Updates
A bulk alt-text update can end in a message that says "done" while some rows were never applied. This article proposes a way to prevent that. Keep one outcome record per row and pick retries from that record, never from memory or from the original file. Everything below is a design proposal. Adapt it to your own tools.
The core idea
Treat the upload file as the plan and the ledger as the truth. Each input row gets exactly one ledger line, and the batch summary is computed from the ledger. It is never typed by hand.
Row outcome ledger
Suggested columns:
- row_number: position in the original file, never renumbered
- image_id: the identifier you match on
- intended_alt: the text you meant to apply
- outcome: one of applied, failed, skipped, unknown
- reason: short plain text, empty for applied
- attempt_count: starts at 1
- last_attempt_at: a timestamp
The outcome "unknown" matters most. If a request timed out, you cannot say whether the change landed. Do not label it failed or applied. Mark it unknown and verify it by reading the current alt text back.
Retry selection worksheet
Before any retry, answer these in order.
- Count rows per outcome. The counts must add up to the number of input rows. If they do not, stop. The ledger is incomplete.
- Take the unknown rows first. Read the live value for each. If it equals intended_alt, change the outcome to applied. If not, change it to failed.
- Group the failed rows by reason.
- Decide per group:
- Missing image identifier: do not retry. Fix the source data, then add a new row.
- Empty or over-length text: do not retry. Correct the text first, and record the corrected text as a new intended_alt.
- Temporary or connection reasons: retry unchanged.
- Reason not understood: retry one row alone before the group.
- Retry only the selected rows. Increment attempt_count and append the new outcome. Do not overwrite the earlier line, so the history stays readable.
Hypothetical worked example
Imagine a file of 40 rows. After the first run the ledger shows 31 applied, 6 failed and 3 unknown. The total is 40, so it passes the first check. You read back the three unknown rows. Two already match, so they become applied. One does not, so it becomes failed. Now you have 33 applied and 7 failed. Of the failed, 4 report a temporary reason, 2 have an image identifier that matches nothing, and 1 has text longer than your limit. You retry the 4 unchanged. You send the 2 unmatched rows back to whoever owns the identifiers, and you shorten the long text and retry it. Say 3 of the 4 succeed on retry. The final report reads: 36 applied (plus the shortened one, if it succeeds), 1 failed and awaiting retry, 2 skipped pending source fixes. It does not read "40 processed".
Branching choices
- If most failures share one reason, pause and investigate that reason before retrying. A blind retry may repeat the same failure.
- If failures are scattered, retry the selected group once.
- If you cannot read live values back, keep unknown rows labelled unknown in the report. Do not promote them.
Stop conditions
Stop retrying a row after a small number of attempts you set in advance, for example three. Stop the whole batch if the second pass fails for the same reason on every row. Stop if the ledger counts stop adding up.
Acceptance checks
- Every input row appears once in the ledger, with an outcome.
- The summary shows applied, failed, skipped and unknown counts, and they sum to the input total.
- No row is marked applied unless it was confirmed, either by a success response or by reading the value back.
- The retry set is a strict subset of failed and unknown rows. Applied rows are never resent.
- The final report states how many rows remain unresolved, even when that number is zero.