Automation Pipeline4 min read

I Queried the Playlist I Had Just Created — It Did Not Exist

Create a YouTube playlist via the API and list its items in the same breath: 404. The real accident comes next — the crash landed before the state save, so the bot was one week away from creating the same playlist again, every week. The fix wasn't a retry. It was not reading at all.

#automation-pipeline#youtube#gotchas#api#reality-check
Left: insert playlist returns an id, then listing its items immediately returns 404 playlistNotFound. Right: the crash lands before the state file is saved, so next week the bot creates the same playlist again.
The 404 itself is propagation delay. The accident was the crash taking an unsaved ID down with it.

A bot runs one of my YouTube channels. It uploads weekly, and once a series has two or more videos it creates a playlist to group them. One week, this was at the bottom of the log.

[playlists] created: ... PL9x•••••••••
googleapiclient.errors.HttpError: <HttpError 404 ...
  "The playlist identified with the request's playlistId parameter cannot be found.">

It created a playlist, then immediately listed items using the ID from the response — and was told no such playlist exists. The ID you just handed me, YouTube.

Does your code assume that anything it just created on an external API can be read back right away?

The 404 isn't a lie — it's a time lag

This is read-after-write consistency. The create call succeeded and the ID is real, but the read path hasn't caught up yet. Read it a few seconds later and it's fine. Textbook distributed-systems behavior — and everyone forgets it while writing client code, because the response contained an ID, so surely a lookup must work.

But the 404 is only half the accident. The other half is worse.

The real accident: the crash came before the save

The original order of operations:

  1. Create playlist → receive ID
  2. List existing items with that ID ← crash here
  3. Add videos
  4. Save the ID to the state file ← never reached

Die at step 2 and the ID evaporates with the process. The state file still says "no playlist for this series," so next week the bot dutifully creates the same playlist again. And the week after. Empty shells with identical names pile up on the channel until someone deletes them.

Resources created on an external service have no rollback. The day I went to delete a duplicate and created one more taught a related lesson, but this one is more structural: the ID of an irreversible side effect must be persisted the moment you receive it. A single "save at the end of the batch" is held hostage by every crash in between.

The fix: not a retry — removing the read

Staring at a 404, the reflex fix is a retry: sleep and re-read, exponential backoff, cap at N attempts… Is that how you would have fixed it?

There's a cheaper road. A playlist you just created is empty. There is nothing to look up. The code died asking the API a question whose answer it already knew.

So the fix points two ways:

  • Save state immediately after creation, so a crash can no longer take the ID with it
  • If the playlist is fresh, skip the existing-items lookup entirely — start from an empty set

No retry loop, no backoff. There's no propagation delay left to race against.

Three self-checks:

  • Do you have code that reads back a resource right after creating it? Is that read actually necessary?
  • How many lines can crash between receiving a created ID and persisting it?
  • If that code crashes and reruns, does it create the resource again?

The honest part

This was actually my second meeting with this bug. It hit a sibling channel running the same engine two weeks earlier, and this channel only needed the fix ported over — clone the same code across channels and you clone the bug once per channel. And one ID from the original crash died unsaved, so the state file contains exactly one entry a human dug out of the logs and typed in by hand. The system got fixed; one piece of the past was still manual labor.

Try one thing today: grep your pipeline for create calls and count how many lines sit between the returned ID and its save. That line count is the distance to your next duplicate.

Related