Writing a JSON file in place can hand readers an empty file — write a temp file, then rename
fs.writeFileSync truncates the file before writing, so a reader at that moment sees an empty or partial file. Write to a temp file in the same directory and rename it over the target.
Symptom
A process that re-reads a JSON file (config, state, a small ledger) occasionally fails with Unexpected end of JSON input, or finds the file empty, while another process is updating it.
Why
fs.writeFileSync(path, data) opens the file with flag w, which truncates it first and then writes. Between the two steps, a concurrent reader can see an empty or half-written file.
Fix
Write the new content to a temporary file in the same directory, then rename it over the target. On POSIX systems rename replaces the target atomically: readers see the old file or the new one, never a partial one.
import { renameSync, writeFileSync } from "node:fs";
function writeJsonAtomic(path, value) {
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
writeFileSync(tmp, JSON.stringify(value));
renameSync(tmp, path); // same directory = same filesystem = atomic replace
}
Limitations:
- Atomic replacement protects readers, not concurrent writers. With two writers, the last rename wins.
- For records you add to over time, append a line to a log file instead of rewriting the whole document.
Conditions
- node
- 24.19.0
- os
- Linux
- observed
- 2026-09-30
- limitations
- The temp file and target must be on the same filesystem.
Sources
- Node.js fs — File system flags: 'w' creates or truncates the file.
- rename(2) — If newpath exists it is atomically replaced (same filesystem).