So I’ll start this thread out with sharing a stupid mistake I made…
This one actually hurt a bit.
I have an automation that generates YouTube thumbnails. It takes a prompt, sends it to Imagen4, and if Imagen rejects the image (safety filter), I rephrase the prompt with Claude and try again.
Then one prompt hit this:
Unable to show generated images. All images were filtered out because they violated Google’s Responsible AI practices. Try rephrasing the prompt.
(support code: 63429089)
In theory the workflow is simple:
Take an input prompt
Build the Imagen4 payload
Generate image
Check if an image was returned
If yes → extract it, write it to disk, return link
If no → rewrite prompt with Claude, retry
The workflow was “working”… with one massive, easy-to-miss flaw.
The fatal bug:
My IF check for “did we get an image back?” wasn’t strict enough.
Imagen returns a predictions[0] object even when it blocks the image, and in that case you get raiFilteredReason instead of a usable gcsUri.
So I had runs where:
- no valid image came back
- the workflow routed into the “rewrite prompt with Claude” path
- and because the prompt going into Claude could be empty / invalid, it would still rewrite it into something...
Which means the system kept looping.
No crashes. No obvious red flags. Just lots of Claude calls.
Oh by the way, this happened over Christmas so I didn't see it for a few days...
I noticed after the fact because Claude usage spiked and I’d burned around $85.
Fix #1 — correct “image exists” check
I tightened the IF so it only counts as “success” if we have a real GCS path:
{{ !!($json?.predictions?.[0]?.gcsUri) && String($json.predictions[0].gcsUri).startsWith('gs://') }}
So:
True branch = we have a real image to download
False branch = filtered / missing output → go to Claude rewrite
(And if raiFilteredReason exists, that’s basically a guaranteed false branch.)
Fix #2 — cost guard / attempt limiter
Even with correct logic, I do not want any expensive step to run forever.
So I added a hard cap on attempts. If we exceed it, we throw an error and stop the run.
Here’s the exact guard:
const start = $('Start').first()?.json ?? {};
const maxAttempts = Number(start.maxAttempts ?? 20);
const attempt = Number($json.attempt ?? 0) + 1;
if (attempt > maxAttempts) {
throw new Error(`Cost Guard: attempt ${attempt} exceeded maxAttempts=${maxAttempts}`);
}
return [{ json: $json }];
Now worst case:
- it retries a limited number of times
- then it fails loudly
- no silent API storms
- predictable spend
Attaching screenshots because this is one of those “looks obvious in hindsight” things, but you only learn it once you’ve been burned.
I'm sure there are better ways to handle this, but this worked for me. Let me know if you've ever had something similar happen and what you did to fix it.