facebook/docusaurus · error
Heading ids can either be overwritten or migrated, not both
Error message
Heading ids can either be overwritten or migrated, not both at the same time
What it means
Thrown by writeMarkdownHeadingId() when its options object has both overwrite: true and migrate: true. These two modes are mutually exclusive: overwrite regenerates heading IDs from the heading text (discarding any existing ID), while migrate rewrites existing IDs into the target syntax (preserving their value). Permitting both would be ambiguous, so the function rejects the combination up front.
Source
Thrown at packages/docusaurus-utils/src/markdownHeadingIdUtils.ts:149
* Takes Markdown content, returns new content with heading IDs written.
* Respects existing IDs (unless `overwrite=true`) and never generates colliding
* IDs (through the slugger).
*/
export function writeMarkdownHeadingId(
content: string,
options: WriteHeadingIDOptions = {},
): string {
const {
syntax = 'classic', // Maybe we'll want to change this default later?
overwrite = false,
migrate = false,
maintainCase = false,
} = options;
// For now, we have 2 booleans (retro compatible)
// but it could be useful to have a "mode" enum instead?
if (overwrite && migrate) {
throw new Error(
'Heading ids can either be overwritten or migrated, not both at the same time',
);
}
const lines = content.split('\n');
const slugger = createSlugger();
// Parse heading ID trying both syntaxes (classic first, then mdx-comment)
function parseHeadingIdAnySyntax(heading: string) {
const classic = parseMarkdownHeadingId(heading, 'classic');
if (classic.id) {
return classic;
}
return parseMarkdownHeadingId(heading, 'mdx-comment');
}
// If we can't overwrite existing slugs, make sure other headings don't
// generate colliding slugs by first marking these slugs as occupiedView on GitHub (pinned to 3f483e80e3)
Solutions
- Decide which behavior you want: pass --overwrite to regenerate IDs from heading text, or --migrate to preserve existing IDs and only change their syntax — never both.
- If calling the API directly, set exactly one of { overwrite, migrate } to true (or neither for the default preserve-and-fill behavior).
- Re-run the CLI or your script with a single mode flag.
- If you genuinely need both effects (rewrite then change syntax), run the command twice sequentially: first overwrite, then migrate.
Example fix
// before
writeMarkdownHeadingId(content, { overwrite: true, migrate: true });
// after — pick one mode
writeMarkdownHeadingId(content, { overwrite: true });
// or
writeMarkdownHeadingId(content, { migrate: true }); Defensive patterns
Strategy: validation
Validate before calling
function isValidHeadingIdOptions(opts: { overwrite?: boolean; migrate?: boolean }): boolean {
return !(opts.overwrite && opts.migrate);
}
if (!isValidHeadingIdOptions(options)) {
throw new Error('Choose overwrite OR migrate, not both.');
} Type guard
function isSingleHeadingMode(opts: { overwrite?: boolean; migrate?: boolean }): boolean {
return !(opts.overwrite === true && opts.migrate === true);
} Try / catch
try {
writeMarkdownHeadingId(content, options);
} catch (err) {
if (err instanceof Error && err.message.includes('overwritten or migrated')) {
// pick one mode and retry
}
throw err;
} Prevention
- Pass at most one of overwrite / migrate when invoking writeMarkdownHeadingId or the write-heading-ids CLI.
- If you expose these options in a UI, make them mutually exclusive radio buttons.
- Run the CLI twice (overwrite then migrate) if you genuinely need both effects sequentially.
When it happens
Trigger: Invoking writeMarkdownHeadingId(content, { overwrite: true, migrate: true }) — most commonly via the `docusaurus write-heading-ids` CLI with both flags passed, or programmatically when wiring the function into a custom processor.
Common situations: Running `docusaurus write-heading-ids --overwrite --migrate` with both flags. A custom script that merges user options onto defaults where both booleans default to true. Confusion about the difference between the two modes leads to passing both 'to be safe'.
Related errors
- Invalid package manager choice ${packageManager}. Must be on
- Directory already exists at path=${dest}!
- Invalid --syntax value "${options.syntax}". Valid values: ${
- Options --overwrite and --migrate cannot be used together.\n
- Invalid command: ${command}
AI-assisted analysis of facebook/docusaurus@3f483e80e3 (2026-08-12).
Data as JSON: /api/errors/516cddbb5ece0b87.
Report an issue: GitHub.