The Upload Succeeded, the Record Did Not

2026年8月25日2 次浏览来源:Dev.to阅读原文

Originally published on hexisteme notes.

I built a YouTube upload stage for a video pipeline, and the flow looked clean enough on paper: start a resumable session, PUT the file, get back a video ID, verify the upload actually landed the way it was supposed to, then write a local record marking the episode as uploaded.

Four steps, each one depending on the last.

It was the dependency between the last two that turned out to be the problem.

The sequence, and where it breaks Verification here means re-querying the video through after the upload finishes, to confirm the visibility wasn't silently demoted, the upload wasn't rejected, and the metadata actually propagated.

That's a reasonable thing to check — YouTube's upload API can report success at the transport layer while the platform-side processing does something you didn't ask for.

But if that verification call raises, the exception propagates straight up, and the local record — a JSON file I'll call — never gets written.

Not "gets written with an error flag." Never written, period.

By the time that exception fires, though, the video already exists on YouTube.

The PUT succeeded.

The video ID is real.

There's a public (or not-quite-public) video sitting on the channel, and there is exactly nothing on disk that knows about it.

Run the same command again after that, and the guard that's supposed to answer "have I already uploaded this?" — a check for whether exists — sails right through, because it doesn't exist.

The result isn't a retry.

It's a second, completely independent upload of the same video.

What "retries don't duplicate" actually meant The module's docstring said retries don't create duplicate videos.

That line wasn't wrong, exactly — it was scoped narrower than it read.

It was true for retries inside the low-level file-PUT function, which reuses the same resumable session URI on retry, so transport-layer hiccups during the upload itself are genuinely safe to retry.

What it didn't cover was someone re-running the whole command from the outside after a failure.

That's a completely different code path, going through the duplicate guard instead of the resumable session, and nothing about the inner retry safety extended to it.

One sentence in a docstring, two different meanings of "retry," and only one of them was actually protected.

A second copy of the same bug, already live While tracking this down, I found a second instance of the identical root cause already sitting in the same repository.

Before this upload stage existed, five episodes had already gone up through a different path — an MCP tool — and that path never wrote either.

Which meant the brand-new duplicate guard wasn't just vulnerable to a future verification failure; it was already blind to five specific videos that were live on the channel right now.

A single run of the new command, pointed at any of those five, would have found no record, seen no reason to stop, and put a duplicate of each one on the channel.

The defect wasn't a bug in the guard's logic.

The guard's logic was fine.

The defect was that a second path could create the same kind of resource and never tell the first path's bookkeeping about it.

It's worth pausing on how this got past testing at all, because every relevant unit test was green the whole time.

Happy path, the missing-file guard, the public-visibility check, the duplicate guard, even a test asserting that verification throws when it detects a demotion — all covered, all passing.

What none of them checked was what state got left on disk after that exception fired.

Tests tend to ask what a function returns and what it throws.

Whether the right thing got thrown is easy to assert.

What's still sitting on the filesystem once it has been thrown is a different question, and it's one that residual-state bugs like this one live in specifically because nobody's assertion is pointed at it.

The fix: write before you verify, not after The fix moved the checkpoint, not the check.

As soon as the

分享
Baike.dev

baike.dev helps you discover great languages, frameworks, databases, DevOps and cloud-native tools.

Quick links

About

Contribute

Found a great developer tool? Share it with the community.

Submit a tool
© 2026 baike.dev Developer EncyclopediaUpdated daily · Discover great developer tools