Back to News & Insights
Artificial Intelligence October 13, 2026 · 8 min read

Don't Try This at Home: What Happened When I Sent Kiro to Work and Went Gardening

There is a special kind of confidence that comes from an AI development environment that just works....

Don't Try This at Home: What Happened When I Sent Kiro to Work and Went Gardening

There is a special kind of confidence that comes from an AI development environment that just works. You describe a feature, and it writes the Terraform, the Go, the Lambda handlers, the S3 wiring. It deploys. The happy path works. And you think: this is fine. I can go do something else now.

So one afternoon I handed Kiro a task, hit go, and left it running. Right on cue, I was handed a task of my own out in the backyard. So I left Kiro to it and went to deal with mine.

Hours later, my phone buzzed. It was AWS. They had detected a recursive loop between a Lambda function and an S3 bucket on my account, and they had helpfully disabled the function's invocations to stop the bleeding.

I've been building a small serverless recipe service on AWS: a private recipe bank for my family. You throw in a recipe as a photo, a chunk of text, or a URL, and the system extracts it, cleans it up, translates it to Finnish, structures it into ingredients and instructions, and stores it. All Terraform, all serverless, all Go on Lambda. Bedrock does the AI heavy lifting. Kiro does most of the typing.

It's the kind of project that's genuinely fun to build with an AI pair, because the work is mostly plumbing. DynamoDB single-table design, presigned S3 uploads, API Gateway routes, a Step Functions pipeline. None of it is hard. All of it is tedious. Exactly the sort of thing you're happy to delegate.

That afternoon's task wasn't about the recipe itself. It was about extra photos: the pictures you take while cooking, or of the finished bake sitting on the counter. The idea was that any family member could attach their own photos to a recipe, a shot of the dough half-mixed, the cake out of the oven, the plated result. A little photo gallery per recipe.

The one real bit of work there is sizing. A photo straight off a modern phone is several megabytes. You don't want to send that to a thumbnail grid, and you don't want the full original loading in a lightbox either. So for every uploaded photo the system needed to generate two derivatives: A 320px thumbnail for the gallery grid. A ~1024px "display" version for the click-to-enlarge view.

Here's the part worth being precise about: my instruction to Kiro was simply generate those two versions from each uploaded image. That's it. I said nothing about where the derivatives should be stored. I didn't ask for them to be written back next to the original, or into the same bucket, or anywhere specific. That was Kiro's own implementation choice, a perfectly reasonable-looking one, and it's exactly the choice that set the trap. More on that in a moment.

What Kiro built: a user photo gets uploaded to S3. An S3 ObjectCreated notification fires a Lambda. The Lambda reads the uploaded photo, resizes it into the thumbnail and display versions, and writes them back into the same bucket so they're ready to serve.

Kiro implemented it, wired the Terraform, and deployed it to my account. Then, to check the happy path, it uploaded a single test image to the service. And right about then, a few minutes later, my laptop slid into sleep mode, and Kiro went quiet with it. The change was live on AWS, one real image had just landed in the bucket, and the freshly deployed photo pipeline was now running entirely on its own.

The Lambda was triggered by "a new object in the bucket." It generated its derivative images and wrote them back into the same bucket. Those writes were new objects in the bucket. Which triggered the notification. Which fired the Lambda. Which generated derivatives and wrote them back into the bucket. Which triggered the notification.

Each pass wrote two new objects, and each of those two objects triggered its own invocation, which wrote two more. This isn't a slow leak. It's exponential. The only reason my account didn't melt is that AWS has a safety net for exactly this mistake: it detects recursive Lambda↔S3 invocation loops and cuts the function off. That's the email I woke up to.

To be clear, this is a well-known S3 footgun. It has been written about for years. "Don't trigger a Lambda from a bucket and write back to the same bucket without a guard" is practically a commandment. And yet here we were, not because I asked for it, but because the obvious implementation reads so innocently: generate a thumbnail, save it next to the original. Of course you save it next to the original. Where else would it go? That unspoken "of course" is the whole problem. I asked only for the versions, and the natural place to put them quietly closed the loop.

There are a few honest ways to break the loop: Separate buckets. Originals in one bucket, derivatives in another. The trigger bucket never receives the Lambda's own writes. Prefix filtering. Keep everything in one bucket but scope the S3 notification to a prefix the Lambda never writes to. Suffix filtering in the handler. Let the notification fire, but have the handler recognize its own output and bail out immediately.

I ended up using two of them at once, because after causing a runaway loop you stop trusting single points of failure.

First, a naming convention plus a scoped trigger. Uploaded photos are stored with an -orig suffix (-orig.jpg); the derivatives get -thumb.jpg and -display.jpg. The S3 notification is filtered to fire only on the -orig suffix. So the Lambda's own output, which never ends in -orig, simply doesn't match the trigger. The feedback edge is cut at the event source.

Second, a defensive skip in the handler itself, in case some future me misconfigures that notification to be broader:

So even if the trigger were somehow widened, the handler recognizes its own -thumb/-display output and bails immediately instead of resizing it and writing yet another object. On top of that, these user photos live under their own user-photos/ prefix, entirely separate from the originals/ prefix the recipe-extraction pipeline listens to, so the two never step on each other.

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation