{
  "id": 16079132,
  "title": "Missing Product Image After Retry: Prevent Duplicate Object Storage Overwrite",
  "url": "https://urgent.news/2026/10/22/missing-product-image-after-retry-prevent-duplicate-object-storage",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-10-22T17:45:08.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/thomasmoore157/missing-product-image-after-retry-prevent-duplicate-object-storage-overwrite-5hk3"
  },
  "original_language": "en",
  "account": "To avoid overwriting a product image during a retry, assign a unique object-storage key to each render attempt. This prevents a duplicate attempt from writing over the original image. If a genuine regeneration is needed, generate a new unique identifier. Never use a shared filename like \"latest.png\" for either operation. For a tenant-scoped logistics export, publish the image by updating a database pointer after a successful unique upload. If an older integration requires a specific object key, only copy it after the transaction commits and retain the original source until its retention deadline. This approach adds a bit more bookkeeping to resolve the ambiguity. Without object versioning or object lock, overwritten or deleted bytes cannot be recovered from storage alone. Only object metadata can annotate a render, but listing by prefix cannot search that metadata, so it does not serve as a reliable retention ledger. When investigating a missing image, start with three essential identifiers: tenant ID, export ID, and product SKU. If two workers write to the same key (e.g., \"north-17/exp-8842/sku-410/latest.png\"), the later write will overwrite the earlier one. The database may still indicate the first render was approved, while the key now points to the second render. To troubleshoot, join render attempts with their exact object keys and compare the attempt ID used by the export with the embedded ID in the stored key. Do not start by rerunning the image generation process. A network retry will retain the UUID and its idempotency key, while a new render will receive a new UUID, even if the prompt remains unchanged. Another hidden race condition exists behind a stable alias. Without an If-Match conditional write, object storage cannot determine which worker should update the \"latest.png\" file. Resolve the accepted immutable key through the export row, while the alias should be treated as a compatibility artifact rather than the definitive record of truth. This issue is more about retention than upload. Overwriting can erase evidence before the policy allows deletion, while a guessed deletion prefix might also erase another tenant's evidence after the policy states only one render should be present. Both problems originate from an object name performing work that belongs in a transactional state. To manage retention and deletion, store essential information in the database, including tenant ID, export ID, product ID, attempt ID, exact object key, creation time, retention deadline, acceptance state, and deletion state. Before issuing a presigned URL, authorize the tenant against that row. Product images should remain private or signed-only; this pattern is not suitable for permanent public links, public image hosts, or static-site hosting. Metadata can include job and policy labels for inspection, but deletion must target specific database rows and operate on their exact keys. Prefix listing can help reconcile objects with no ledger row or rows whose objects are missing, but it cannot determine which record satisfies a legal hold, erasure request, or contractual retention period. The edge cases can impact capacity. For example, an export containing 40,000 products with up to three render attempts each, retained for 14 days, would result in 120,000 object records before actual retry and completion rates refine the estimate. These figures are only illustrative sizing inputs, not benchmarks. The database index, deletion queue, and reconciliation scan must all handle this level of cardinality. Lifecycle expiry is too coarse for individual policies, as its minimum is one day. For a six-hour preview, an application-owned deletion deadline is necessary, and explicit cleanup is required instead of relying on automatic rules. Define a deletion SLO around policy, not bucket size, by measuring the elapsed time from the eligible deletion date until deletion is verified, and alert on the age of the oldest overdue row. The on-call engineer should focus on the specific tenant's erasure workflow rather than a count of stored bytes. Integrate immutable upload identity in Go using the following program: it creates an immutable attempt key, uploads the corresponding image, and demonstrates that replaying delivery for one attempt keeps the ID and key, while requesting another render creates a new attempt. The code includes HTTP method specification, Bearer authentication from an environment variable, status checks, and bounded exponential backoff that respects Retry-After on 429 responses.",
  "summary": "Prevent a duplicate retry from overwriting a product image: give every render attempt an immutable object-storage key, or the accepted image may be missing after the retry. Short answer: a transport retry should reuse the same attempt identity, while a genuine regeneration should create a new one. Never let either operation write a shared name such as latest.png . For a tenant-scoped logistics…",
  "key_points": [
    "Assign unique object-storage key per render attempt to prevent overwrite.",
    "Update database pointer after successful unique upload for tenant-scoped export.",
    "Store tenant ID, export ID, product SKU, attempt ID, key, and timestamps in database."
  ],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}