tinyhumansai/openhuman · warning · Error

Refusing to overwrite hidden file: ${outputPath}

Error message

Refusing to overwrite hidden file: ${outputPath}

What it means

A write-guard in main(): when --output names a path that already exists AND basename(outputPath) starts with '.' (a dotfile such as .env, .gitignore, .release-notes.md), the script refuses to overwrite it, protecting hidden config files from being clobbered by generated Markdown.

Source

Thrown at scripts/release/generate-release-notes.mjs:703

  });
  const title = releaseTitle(from, options.to, resolvedTo);

  let markdown;
  if (options.dryRun) {
    const request = buildOpenAiRequest({ model: options.model, title, payload });
    markdown = JSON.stringify(request, null, 2);
  } else if (options.noAi) {
    markdown = renderDeterministicNotes({ title, payload });
  } else {
    const request = buildOpenAiRequest({ model: options.model, title, payload });
    markdown = await summarizeWithOpenAi(request);
    markdown = ensureAllPullRequestsLinked(markdown, payload.pullRequests);
  }

  if (options.output) {
    const outputPath = resolve(options.output);
    if (existsSync(outputPath) && basename(outputPath).startsWith('.')) {
      throw new Error(`Refusing to overwrite hidden file: ${outputPath}`);
    }
    writeFileSync(outputPath, markdown.endsWith('\n') ? markdown : `${markdown}\n`);
    console.error(`[release-notes] Wrote ${outputPath}`);
  } else {
    process.stdout.write(markdown.endsWith('\n') ? markdown : `${markdown}\n`);
  }
}

if (import.meta.url === pathToFileURL(process.argv[1]).href) {
  main().catch((error) => {
    console.error(`[release-notes] ${error.message}`);
    process.exit(1);
  });
}

View on GitHub (pinned to a221052e0d)

Solutions

  1. Point --output at a regular, non-hidden filename (e.g. release-notes.md)
  2. If overwriting the hidden file is truly intended, remove or rename it yourself first — the script will not do it

Example fix

# before (hidden draft already exists → refused)
node scripts/release/generate-release-notes.mjs --output .release-notes.md

# after
node scripts/release/generate-release-notes.mjs --output release-notes.md
Defensive patterns

Strategy: validation

Validate before calling

import { basename, resolve } from 'node:path';
import { existsSync } from 'node:fs';
const out = resolve(options.output);
if (existsSync(out) && basename(out).startsWith('.')) {
  throw new Error(`refusing hidden output ${out}; choose a visible filename`);
}

Prevention

When it happens

Trigger: Passing --output .env, --output .draft.md, or any path where a hidden file already exists — typically a scripted or copy-pasted output path that collides with a dotfile. Writing a new hidden file is fine; only overwriting an existing one is refused.

Common situations: Shell variables or globs resolving to a dotfile; a stale hidden draft from an earlier experiment still sitting at the target; muscle-memory use of dot-prefixed scratch filenames.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/6ca449d53051ad7d. Report an issue: GitHub.