Why our AI-generated video ads were inventing logos, wordmarks, and construction details on real products — and how switching to real catalog photos, not a hand-written prompt, actually fixed it. A full trace from measurement through fix.
Headline finding: the fix that actually scales isn't a cleverer prompt — it's simpler than that. Feed the model real, undegraded catalog photos instead of the padded/AI-outpainted stack production has been building, and a fully generic prompt (no per-product authoring at all) gets every headline defect fixed. A hand-written, per-product prompt was tried first and looked like the answer — it wasn't. See Section 5.
Pelagic Gear's video ads were being judged as roughly 20% pass. That number was measuring the wrong thing.
A derive is a free crop/retitle of an already-existing master into a different aspect ratio — it never generates new pixels. Splitting the two out:
| Pass rate | |
|---|---|
| Real generations (masters) | 62% 8 of 13 judged |
| Derives of those masters | 16% 24 of 148 |
Worse: 44 derives of a passing master still failed, and 9 derives of a failing master passed. Production QC on a derive is largely noise — the ~20% headline number was a derive statistic, not a generation-quality statistic.
Production's own quality check judges each video against the reference stack it was given — which is itself padded, cropped, or AI-outpainted, not the real product photo. Judging the same 14 masters directly against the native catalog photograph instead:
The code that decides "can we free-crop this reference, or do we have to pay for an AI outpaint" needs YOLO-detected product boxes. The query loading sibling reference images for a video simply never requested that field. Every auto-assembled sibling was forced onto the paid path regardless of whether a free crop was possible.
Found a teammate had already fixed this in a copy of the code that isn't the one actually running in production. Fixed in the live renderer, deployed.
The product-only seed image was padded to the video's delivery resolution (as small as 720×720 for the product itself) instead of the source photo's own resolution — throwing away most of the real detail before the video model ever saw it.
Fixed — pad now derives its canvas from the source image, preserving up to 3× more real pixels of the product.
Counting every defect found across the 14-ad blind pass by the video timestamp it appeared at: the frame at the midpoint — exactly where the prompt says "slow zoom toward the most distinctive product detail" — carried 6 of 11 total defects. The zoom wasn't panning across real pixels; it was cutting to a newly generated close-up, and that's where the model invented things.
Partially addressed — see the real fix in Section 5, which supersedes the original "just remove the zoom" attempt.
Partway through testing the fixes above, every 10-second/9:16 video regenerate started failing. Three ads produced videos that looked suspiciously identical to their originals. This needed a real answer, not a guess.
The exact same combination that had been generating successfully all day — duration=10, aspect_ratio=9:16 — started returning:
HTTP 500 → "All channels failed. Last error: HTTP 422:
The model 【omni_flash-10s-portrait】 does not exist"
Confirmed by fetching the byte hash of the "new" video and finding it identical to the pre-outage file — Atlas never generated anything new, and critically, never billed for it either. One real generation that landed during the same window (a different ad, retried automatically) proved the persist logic itself was fine — when Atlas returns a real video, it saves correctly. The failure was isolated to one internal Atlas routing channel for that specific duration+aspect combination, confirmed by direct API calls, not inferred.
A retry policy was also caught and stopped mid-flight — it was about to auto-resubmit two more ads into the same dead channel.
The outage forced the question: could we bypass Atlas entirely and call the underlying model directly? Turns out yes — Atlas is a thin wrapper over Google's own gemini-omni-flash-preview model, and a direct API key was already available and already in use elsewhere in the codebase for other purposes.
Same three-image stack the production pipeline builds for every ad — shown here so the comparisons below are legible against real inputs, not assumed ones.



| Arm | What happened | Verdict |
|---|---|---|
| Gemini, reference_to_video (all 3 images) | Invented a side-profile camera angle at ~2.5s that doesn't exist in any reference. Wordmark stayed correct, just softer than Atlas. | Angle not a defect — see Section 5 |
| Gemini, image_to_video (seed image only — this task type can't take 3 images) | Stayed disciplined on camera framing, but regenerated the seed frame itself and corrupted the product's own wordmark. | Worse fidelity |
| Grok Imagine (all 3 images, new short prompt) | Correct marks, slightly softer than Atlas. Also settled a real pricing question — the catalog page and our own cost assumption were both wrong. | Clean / minor |
Same product, same prompt, three genuinely different outcomes — proof that the model, not just the reference prep, is a real variable in fidelity.



That single correction changed everything. Camera motion, subject motion, new viewpoints — none of it counts as a defect anymore. The only thing that disqualifies a video: an invented, altered, or missing detail on the product itself, checked against the real catalog photo.
The first fix tried was a hand-written prompt block per product, naming the real letters and — where a prior attempt had already failed — the exact garbled text to avoid. It worked, mostly:
| Ad | Was | With hand-written marks |
|---|---|---|
| Vaportek hoodie | Invented competitor label "JANA AARE" | Fixed |
| Mako shorts | Garbled tagline "BUILT FOR PISRING" | "Fixed" the wrong way — see below |
| Vaportek hoodie (16:9) | Wrong logo, reversed fish, brand name missing | Fixed |
| Vaportek hoodie (chest + sleeve) | Unboxed logo + invented sleeve print | Chest fixed — sleeve unchanged |
The sleeve print refused to clear no matter how the prompt was worded — the block explicitly said "forearms otherwise blank" and the model still copied a stacked invented print onto the sleeve. The reason: that print was already baked into the reference photo itself — a leftover artifact from an earlier AI-outpaint step. No prompt instruction can override what the model is literally shown in a picture.


Worse, on a second product the hand-written marks were factually wrong: the block said the waistband had "NO second line," but the real catalog photo genuinely shows BUILT FOR FISHING as a second line. The manual authoring had suppressed something that was actually true.

Re-running all four ads with zero product-specific prompt text — a fully generic instruction, paired with the real, undegraded catalog photos instead of the padded/outpainted stack — fixed every remaining defect, including the sleeve the hand-written version couldn't touch:
| Ad | Clean photos + generic prompt |
|---|---|
| Vaportek hoodie — neck label | Clean |
| Mako shorts — waistband | Clean, and matches the real catalog detail photo exactly |
| Vaportek hoodie — chest + sleeve | Both clean — the sleeve the other approach couldn't fix |
| Vaportek hoodie (16:9) — logo + sleeve | Both clean |




To make sure this wasn't specific to the four ads already under a microscope, the same clean-photo + generic-prompt approach was run on Leaderman — a product that had already passed under the old pipeline, used here as a check that the new approach doesn't regress a product that was already fine.

Mostly clean, with one honest miss worth keeping in the record: the waistband and construction came through perfectly, but a secondary hem tag came out with the wrong layout — the real tag reads "boxed P + fish icon, then PELAGIC below it"; the generated one swapped the order and dropped the box entirely.


Clean photos plus a generic prompt gets you most of the way there — it fixed the hardest marks on every product tested — but it isn't a guaranteed clean sweep on every secondary detail. A mark the prompt never specifically calls out can still drift.
Yes — and it's simpler than the first attempt suggested. The path that actually works needs no per-product authoring at all.
The remaining worry was real: if you want to tell the model which of the 3 photos to trust for fine print, don't you need to run a new vision pass on every catalog image first? Checked against production data: that classification already exists. Every catalog image already carries a label (plain product photo / on-model / detail shot), written once by an existing classifier and reused forever — verified directly against a real product's own 3 submitted photos, where the stored labels matched exactly what those photos show. A second existing field (extracted on-image text) already answers which photo has the readable branding.
One real caveat, also verified: on a different product, "detail" turned out to mean a fabric close-up, not a logo — so the fix reads the extracted text on the selected photo rather than trusting the label name alone. With that adjustment, no new spend is needed for this at all.
Tried explicitly staging the three photos by role — "use this one for the wide shot, use that one for fine print" — on top of the hand-written-marks approach. It worked cleanly, but a direct side-by-side against skipping staging entirely (clean photos, no staging, no per-product marks) showed the marks were reproduced just as accurately either way. Staging is a nice-to-have, not a requirement.
No. Checked against production data: every catalog photo already carries a label (plain product photo / on-model / detail shot), written once by an existing classifier and reused forever — verified directly against a real product's own 3 submitted photos, where the stored labels matched exactly what those photos show. A second existing field (extracted on-image text) already answers which photo has the readable branding, with one caveat: "detail" sometimes means a fabric close-up, not a logo, so the fix should check the extracted text, not just the label name. Either way, this is a code change reading data that already exists — not a new cost.
| What | Cost |
|---|---|
| Direct Gemini — Leaderman comparisons, both fix attempts across 4+1 ads, staging test, retry | ~$14.50 |
| Grok Imagine — 2 real generations (first hit a length cap, cost $0) | ~$1.43 |
| Atlas — one real successful generation during the outage window | ~$0.90 |
| Total settled | ~$16.83 |
All spend tracked against a $50 approved budget for this investigation; nothing close to the limit. No billable request was ever retried after a failure — every failed attempt above cost $0, confirmed directly against each provider's own settlement record. One transient (unbilled) API error was retried once, which is why the count of real generations is slightly higher than the ad count.