CLAUDE LABJP
WWDC — WWDC 2026 confirms Siri runs on Google Gemini; third-party handoff to ChatGPT is dropped, and Siri AI won't ship in the EU under the DMA at iOS 27BILLING — 6 days until the Jun 15 change: Agent SDK, headless Claude Code, GitHub Actions, and third-party agents move to API-rate monthly creditOUTAGE — claude.ai, Claude Code, and Cowork saw an outage (Jun). Scheduled runs are safest when built around fallbackModel and retriesDYNAMIC-WORKFLOWS — Dynamic workflows are on by default on Max/Team and the API, for codebase-wide bug hunts and independent verificationULTRACODE — Claude Code's new ultracode setting sits in the effort menu, fixing effort to xhigh while Claude decides when to run a workflowOPUS4.8 — Claude Opus 4.8 is settled in as the default across major plans, with stronger coding, agentic, and reasoning skillsWWDC — WWDC 2026 confirms Siri runs on Google Gemini; third-party handoff to ChatGPT is dropped, and Siri AI won't ship in the EU under the DMA at iOS 27BILLING — 6 days until the Jun 15 change: Agent SDK, headless Claude Code, GitHub Actions, and third-party agents move to API-rate monthly creditOUTAGE — claude.ai, Claude Code, and Cowork saw an outage (Jun). Scheduled runs are safest when built around fallbackModel and retriesDYNAMIC-WORKFLOWS — Dynamic workflows are on by default on Max/Team and the API, for codebase-wide bug hunts and independent verificationULTRACODE — Claude Code's new ultracode setting sits in the effort menu, fixing effort to xhigh while Claude decides when to run a workflowOPUS4.8 — Claude Opus 4.8 is settled in as the default across major plans, with stronger coding, agentic, and reasoning skills
Articles/Claude Code
Claude Code/2026-06-09Intermediate

When Yesterday's Article Bleeds Into Today's — The Stale Fixed-Name Temp File Trap

In a Claude Code automation pipeline, a fixed-name temp file kept stale content from the previous run and leaked old body text into a completely different article. Here is why the write fails silently, and how unique names plus a post-write grep check prevent it.

troubleshooting118error24fix19claude-code165automation95shell2

One morning I was re-reading a published post and felt a chill. An article about Claude's MCP had, sitting right in the middle of its body, an entire paragraph from the previous day's piece about a git error. The title and intro were correct. Only the middle of the body belonged to a different article — and the logs showed no error at all.

A bug that prints red text is one you can catch. What makes this kind of incident frightening is that every step reported success while the content quietly swapped itself out. The cause turned out to be the name of a temp file my automation relied on.

What was happening — only the body was from an old article

My article generation does not stuff a long MDX body into a shell variable or a single heredoc. It writes the body to a temp file first, then concatenates it with the frontmatter. Written naively, it looks like this:

# write the body to a temp file
cat > /tmp/body.md << 'BODY'
(this run's article body)
BODY
 
# assemble the article by joining frontmatter and body
cat frontmatter.md /tmp/body.md > "content/articles/en/claude-code/${SLUG}.mdx"

At a glance nothing seems wrong. But the fact that /tmp/body.md is a fixed name came back to bite me later.

When I generated several articles in a single run, if the step that wrote the second body failed for any reason, /tmp/body.md still held the first article's body. The concatenation step ran anyway, and the first article's body ended up inside the second article.

Why the write fails "silently"

A cat > file << 'BODY' ... BODY heredoc can finish without writing the content under several conditions, and often it slips by without a loud, fatal error.

One case is a misaligned heredoc terminator. If your body happens to contain a line that reads BODY, or you indented the terminator, the shell loses track of the end and writes only part of the content — or nothing.

Disk exhaustion is another classic. If free space runs out mid-write, the file is closed in a half-finished state. The /tmp directory in sandboxes and CI is often small, so this is an especially easy mine to step on. I covered the space side of this in handling ENOSPC when you run out of disk.

Permissions matter too. If /tmp is owned by a different user than the process, you cannot write — yet you can still read the old file. I dealt with that ownership situation in falling back to $HOME/repos when /tmp is not writable.

The common thread is this: when a write fails, the file that existed before it does not disappear. cat > truncates the file before writing, so a failure midway leaves half-written content; and if the redirect never runs at all, the previous content simply stays. The next step never checks the file's freshness, so it happily reads the stale leftovers.

Reproducing it

Confirming the behavior by hand makes the fix obvious. Leave old content in a fixed-name file, then deliberately fail the next write.

# write the first article's body
echo "OLD ARTICLE BODY" > /tmp/body.md
 
# fail the second write on purpose, via a nonexistent directory
cat > /tmp/nowhere/body.md << 'BODY' 2>/dev/null
NEW ARTICLE BODY
BODY
echo "write exit code: $?"   # returns 1 (but the script keeps going)
 
# reading /tmp/body.md without a freshness check...
cat /tmp/body.md             # -> prints OLD ARTICLE BODY

Even though the write failed (exit code 1), if the assembly step reads /tmp/body.md, the old body leaks in. Even set -e does not help detect the leftover in /tmp/body.md when the failed redirect targeted a different path.

The fix — "change the name every time" and "verify what you wrote"

What I settled on combines two principles: a unique name to prevent it, and a post-write check to catch it when it slips through.

1. Give the temp file a unique name containing the slug

Drop the fixed name and use a name that includes the article slug (and a random component if needed). If you never reuse the same path as the previous leftover, the leak cannot happen structurally. Move the location to $HOME, where space and ownership are stable.

# build a per-article path. never use the fixed name /tmp/body.md
WORKDIR="$HOME/.cache/article-build"
mkdir -p "$WORKDIR"
BODY_FILE="$WORKDIR/${SLUG}.body.md"
 
# delete any old file before assembling, just in case
rm -f "$BODY_FILE"
 
cat > "$BODY_FILE" << 'BODY'
(this run's article body)
BODY

mktemp works well too. BODY_FILE=$(mktemp) reliably gives you a collision-free temp file. In automation, though, embedding the slug in the name makes it easier to trace which file belongs to which article when reading logs.

2. Right after writing, check both the exit code and the contents

Even with a unique name, a failed write leaves an empty or half-written file. So immediately after writing, inspect both the exit code and the file contents.

cat > "$BODY_FILE" << 'BODY'
(this run's article body)
BODY
STATUS=$?
 
# (a) check the exit code
if [ "$STATUS" -ne 0 ]; then
  echo "write of body failed (exit $STATUS)"
  exit 1
fi
 
# (b) confirm it is non-empty and contains a phrase unique to this article
if [ ! -s "$BODY_FILE" ]; then
  echo "body file is empty: $BODY_FILE"
  exit 1
fi
if ! grep -q "a phrase that uniquely identifies this article" "$BODY_FILE"; then
  echo "expected body not present. could be a stale leftover"
  exit 1
fi

3. Verify the assembled MDX with grep as well

As a final step, confirm the assembled MDX is actually the right article. Check that a keyword tied to the frontmatter title or slug appears in the body, and that no word unique to a different article has crept in.

OUT="content/articles/en/claude-code/${SLUG}.mdx"
 
# the slug is useful for cross-checking frontmatter against the body
if ! grep -q "slug: \"${SLUG}\"" "$OUT"; then
  echo "slug in the generated MDX does not match"
  exit 1
fi
 
# optionally, ensure no marker from the just-generated previous article leaked in
PREV_MARKER="a heading unique to the previous article"
if grep -q "$PREV_MARKER" "$OUT"; then
  echo "another article's body may have leaked in"
  exit 1
fi

I wrote about this idea of a pre-push grep guard from another angle in adding pre-push grep guards to your SKILL.md. And if your searches come up empty when you expect a match, diagnosing why grep returns zero results will help you make these checks more reliable.

In automation, suspect the files that carry state across runs

What this incident reminded me of is that the riskiest thing in automation is a file that survives across multiple runs. A fixed-name temp file is convenient, but it silently carries the previous state into the next run. Just as git push succeeds even when it does nothing, a file's freshness is a different dimension from a command's success. I wrote up that failure of trusting exit codes alone in the trap where git push reports success but nothing is pushed.

I'm Masaki Hirokawa, an artist and creator. I have been an indie developer since 2014, running apps with over 50 million cumulative downloads alongside the automated updates of four technical blogs (Dolice Labs). When I make something with my hands, the failure I fear most is not the loud one but the quiet one — something incorrect being built without a sound. My grandfather was a temple carpenter, and remembering his work, I want to confirm, as part of the system itself, that what I meant to write is truly there.

So pick one fixed-name temp file in your automation, rename it to something unique that includes the slug, and add a single grep -q right after the write. The next time a "success" log appears, you will be able to trust it down to its contents.

Thank you for reading to the end. I would be glad if even one person is spared the pale shock of a silent swap.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude Code2026-06-03
When git push Says Success but Nothing Lands — the Silent commit Failure in Claude Code
git push prints Everything up-to-date and exits zero, yet your changes never reach the remote. Here is why commit silently fails on a fresh sandbox clone, and how to verify a real push with SHA comparison.
Claude Code2026-05-02
Stop the 'old_string is not unique' Error in Claude Code's Edit Tool — A Practical Rewrite Strategy
When Claude Code's Edit tool throws 'old_string is not unique in the file', the root cause is usually that the prompt didn't guarantee a uniquely identifiable target. Here's how to design rewrites that actually stick.
Claude Code2026-04-25
When Claude Code Hooks Loop Forever — Stopping Self-Triggering PostToolUse Hooks
You wired up Claude Code hooks and now your terminal is a waterfall of tool calls that won't stop. Here's why PostToolUse hooks loop on themselves, and the patterns I use in real projects to make sure they never do again.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →