windmill-labs/windmill · error
Could not find a free recording file name
Error message
Could not find a free recording file name
What it means
Exhaustion guard in writeRecording (app dev session recorder): it tried 100 candidate recording filenames — recordingFileName(now, attempt) for attempt 0..99 — and every one hit an existing file (the 'wx' exclusive-create flag fails when the name is taken). With per-attempt distinct names, hitting all 100 means something pathological: a directory pre-filled with recordings sharing the same timestamp base, or repeated write failures being misread as existence collisions. The input at fault is the recordings directory's existing contents.
Solutions
- Clean or archive old recording files in the app's recordings directory so fresh names are available
- Check for the real failure mode: if the directory is unwritable, fix permissions on recordingsDir instead of relying on retries
- If caused by an external process creating files with identical naming, change the recording directory for the dev session
Defensive patterns
Strategy: retry
When it happens
Trigger: Thrown at cli/src/commands/app/dev.ts:778 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/f7b3944f6a9ba081.
Report an issue: GitHub.
Appendix: source
Thrown at cli/src/commands/app/dev.ts:778
}
/** Write the recording under a name nothing else holds. `wx` is what makes
* this safe: two tabs stopping in the same millisecond both create, and the
* loser retries rather than overwriting the winner. */
async function writeRecording(body: string): Promise<string> {
const now = new Date();
for (let attempt = 0; attempt < 100; attempt++) {
const file = recordingFileName(now, attempt);
try {
await fs.promises.writeFile(path.join(recordingsDir, file), body, {
flag: "wx",
});
return file;
} catch (error: any) {
if (error?.code !== "EEXIST") throw error;
}
}
throw new Error("Could not find a free recording file name");
}
function saveRecording(req: http.IncomingMessage, res: http.ServerResponse) {
if (!isOwnOrigin(req.headers.origin, req.headers.host)) {
sendJson(res, 403, { error: "Cross-origin recording upload refused" });
return;
}
const chunks: Buffer[] = [];
let size = 0;
let refused = false;
// An upload cut short (by the refusal below, or by the browser) raises
// 'error' on the request, which unhandled takes the dev server down.
req.on("error", (error: Error) => {
if (!refused) {
log.warn(colors.yellow(`Recording upload failed: ${error.message}`));
}
});
req.on("data", (chunk: Buffer) => {View on GitHub (pinned to e474e8803c)