Skip to content

Fix quarto preview subdir/file.qmd crashing with doubled path#14150

Merged
cscheid merged 2 commits intomainfrom
fix/preview-path-doubling
Mar 3, 2026
Merged

Fix quarto preview subdir/file.qmd crashing with doubled path#14150
cscheid merged 2 commits intomainfrom
fix/preview-path-doubling

Conversation

@cderv
Copy link
Collaborator

@cderv cderv commented Mar 3, 2026

quarto preview subdir/page.qmd in a website project crashes with readfile subdir\subdir\page.qmd — a doubled path. quarto render works fine.

Root Cause

Preview makes two render calls: renderFormats (receives the original relative path) then renderProject (normalizes to absolute). The first call creates a cached ExecutionTarget with source: "subdir/page.qmd". The second gets a cache hit with the stale relative source.

projectPath() was designed for output filenames — it resolves relative paths via join(dirname(target.source), path).

const projectPath = (path: string) => {
if (context.project) {
if (isAbsolute(path)) {
return relative(
normalizePath(context.project.dir),
normalizePath(path),
);
} else {
return relative(
normalizePath(context.project.dir),
normalizePath(join(dirname(context.target.source), path)),
);
}
} else {
return path;
}
};

But then, context.target.source itself is passed through this function.

input: projectPath(context.target.source),

When target.source was relative, dirname + join doubled the subdirectory:

join(dirname("subdir/page.qmd"), "subdir/page.qmd") → "subdir/subdir/page.qmd"

projectPath(finalOutput!) is used correctly though — output filenames like page.html do need the dirname + join resolution.

: projectPath(finalOutput!),

Fix

Split projectPath into two functions with distinct responsibilities:

  • projectRelativeInput(sourcePath) — for the source file path. Uses normalizePath directly, which handles both relative and absolute paths, because our normalizePath function does make path absolute if not.
  • projectOutputPath(path) — for output filenames (like page.html). Preserves the existing dirname + join logic since output names are relative to the source directory.

Bisect

Last good: v1.9.17. First bad: v1.9.18 (77633b3).

Related

Follow up on preview fixes that introduced this

which was a regression of broader quarto preview behavior change

Also, in a way related to this observation we've done since new single project context is out

cderv added 2 commits March 3, 2026 17:29
`quarto preview subdir/page.qmd` in a website project crashes with
`readfile subdir\subdir\page.qmd`. The `projectPath` function was
designed for output filenames (resolving relative to source directory
via dirname+join), but was also called with the source path itself.
When that source path was relative, dirname+join doubled the subdirectory.

Split into two functions: `projectRelativeInput` for source paths
(normalizes directly) and `projectOutputPath` for output filenames
(preserves the dirname+join logic).
The FileInformationCacheMap (added in #13955) normalizes keys so that
relative and absolute paths to the same file share one cache entry.
This adds the test case that was missing from the original test suite.
@posit-snyk-bot
Copy link
Collaborator

posit-snyk-bot commented Mar 3, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@cderv
Copy link
Collaborator Author

cderv commented Mar 3, 2026

@cscheid tests are passing, and I think this is a change that make sense and solve the problem.

I can now run preview correct.

So if good to you, please merge and build a new prerelease.

@cscheid cscheid merged commit 4e46c31 into main Mar 3, 2026
51 checks passed
@cscheid cscheid deleted the fix/preview-path-doubling branch March 3, 2026 17:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants