Skip to content

margin-footer / margin-header with markdown/HTML content crashes on Windows with os error 123 #14446

@wainwright1000

Description

@wainwright1000

Bug description

On Windows, setting margin-footer (or margin-header) to inline markdown or HTML content that contains characters illegal in Windows paths causes quarto render to crash with os error 123.

This happens because websiteProjectConfig (src/project/types/website/website-config.ts) calls resolveProjectPaths on each margin-footer value, and resolveProjectPaths does a raw existsSync(maybePath) check. On Windows, existsSync throws when the string contains characters like [, ], <, >, |, :, ", ?, *.

On macOS/Linux, existsSync simply returns false for these strings, so the bug is Windows-specific.

Steps to reproduce

Create a Quarto book project with the following _quarto.yml:

project:
  type: book

book:
  title: "Demo"
  margin-footer: |
    [![](/img/logo.png)](https://example.com)
    <link rel="preload" href="/img/logo-dark.png" as="image">

format:
  html:
    theme: cosmo

Then run quarto render index.qmd --to html on Windows.

Expected behavior

Quarto should render without error, treating the margin-footer value as inline markdown/HTML content (since it does not exist as a file).

Actual behavior

Render fails with:

ERROR: The filename, directory name, or volume label syntax is incorrect. (os error 123): stat '[![](/img/logo.png)](https://example.com)
<link rel="preload" href="/img/logo-dark.png" as="image">

Stack trace points to resolveProjectPaths in websiteProjectConfig.

Root cause

In website-config.ts:

const resolveProjectPaths = (maybePath: string) => {
  if (existsSync(maybePath)) {
    return join(projectDir, maybePath);
  } else {
    return maybePath;
  }
};

existsSync should be replaced with safeExistsSync (which Quarto already uses elsewhere, e.g. in expandMarkdownFilePath) to gracefully handle invalid paths on Windows.

Workaround

Move the content to a separate file and reference it:

margin-footer: "margin-footer.md"

This works because the string is a valid file path, so existsSync succeeds, and Quarto's expandMarkdownFilePath correctly reads the file content later.

Quarto check output

Quarto 1.9.36
[✓] Checking versions of quarto binary dependencies...
      Pandoc version 3.4.0: OK
      Dart Sass version 1.80.5: OK
      Deno version 2.2.2: OK
      Typst version 0.13.0: OK
[✓] Checking versions of quarto dependencies......OK
[✓] Checking Quarto installation......OK
      Version: 1.9.36
      Path: C:\Users\Andrew\AppData\Local\Programs\Quarto\bin
[✓] Checking tools....................OK
      TinyTeX: (not installed)
      Chromium: (not installed)
[✓] Checking LaTeX....................OK
[✓] Checking basic markdown render....OK
[✓] Checking R installation...........OK
      Version: 4.5.0
      Path: C:/PROGRA~1/R/R-45~1.0
      LibPaths:
        - C:/Program Files/R/R-4.5.0/library
      knitr: 1.50
[✓] Checking Knitr engine render......OK

Your environment

  • OS: Windows 11
  • Quarto: 1.9.36

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingtriaged-toIssues that were not self-assigned, signals that an issue was assigned to someone.windows

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions