slidevjs/slidev · error · Error

Code snippet path escapes the project root: ${src}

Error message

Code snippet path escapes the project root: ${src}

What it means

Thrown by resolveSnippetImport when the resolved absolute path of a <<< snippet import is not inside any of allowedRoots (userRoot + workspace root + extra roots). isPathInsideRoots rejects any path containing '..' traversal that escapes the root set. This is a security guard preventing deck files from reading arbitrary files outside the project.

Source

Thrown at packages/slidev/node/syntax/snippet.ts:128

export function resolveSnippetImport(lineText: string, userRoot: string, slide: SlideInfo, allowedRoots: string[] = [userRoot]) {
  const match = lineText.trimStart().match(RE_SNIPPET_IMPORT)
  if (!match)
    return null

  let [, filepath = '', regionName = '', lang = '', meta = ''] = match
  const dir = path.dirname(slide.source.filepath)
  const src = slash(
    filepath.startsWith('@/')
      ? path.resolve(userRoot, filepath.slice(2))
      : path.resolve(dir, filepath),
  )

  lang = lang.trim() || path.extname(filepath).slice(1)
  meta = meta.trim()

  if (!isPathInsideRoots(src, allowedRoots)) {
    throw new Error(`Code snippet path escapes the project root: ${src}`)
  }

  const isAFile = fs.existsSync(src) && fs.statSync(src).isFile()
  if (!isAFile) {
    throw new Error(`Code snippet path not found: ${src}`)
  }

  let content = fs.readFileSync(src, 'utf8')

  if (regionName) {
    const lines = content.split(RE_NEWLINE)
    const region = findRegion(lines, regionName.slice(1))
    if (region) {
      content = dedent(
        lines
          .slice(region.start, region.end)
          .filter(l => !(region.re.start.test(l) || region.re.end.test(l)))
          .join('\n'),

View on GitHub (pinned to 0d798ace58)

Solutions

  1. Move the target file inside the project root (or a configured root) and reference it relatively.
  2. If you legitimately need a parent directory, add it to the Slidev roots config so isPathInsideRoots accepts it.
  3. Use the @/ alias to point at files under userRoot instead of ../ paths.
  4. Check that userRoot/workspace root are correct (entry file location drives userRoot).

Example fix

// before: escapes the project root
<<< ../shared/snippet.ts

// after: keep sources inside the project
<<< @/shared/snippet.ts
// (place the file under <userRoot>/shared/snippet.ts)
Defensive patterns

Strategy: validation

Validate before calling

import { isPathInsideRoots } from '@slidev/cli/node/utils'
import path from 'pathe'
import { slash } from '@antfu/utils'

function snippetInsideRoots(filepath: string, dir: string, userRoot: string, roots: string[]): boolean {
  const src = slash(
    filepath.startsWith('@/') ? path.resolve(userRoot, filepath.slice(2)) : path.resolve(dir, filepath),
  )
  return isPathInsideRoots(src, roots)
}

// before rendering, validate <<< lines:
if (!snippetInsideRoots(filepath, slideDir, userRoot, allowedRoots)) {
  throw new Error(`Snippet escapes roots: ${filepath}`)
}

Type guard

function snippetPathSafe(filepath: string, dir: string, userRoot: string, roots: string[]): boolean {
  const src = slash(
    filepath.startsWith('@/') ? path.resolve(userRoot, filepath.slice(2)) : path.resolve(dir, filepath),
  )
  return isPathInsideRoots(src, roots)
}

Try / catch

try {
  return resolveSnippetImport(line, userRoot, slide, allowedRoots)
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Code snippet path escapes the project root')) {
    // drop the import or relocate the file inside the project
    return null
  }
  throw e
}

Prevention

When it happens

Trigger: Writing <<< ../secrets.env or <<< /etc/passwd in a slide, or any snippet path that, after path.resolve, lands outside every allowed root. Triggered at parse time when markdown-it renders the snippet block.

Common situations: Snippets referencing shared code in a parent directory of the project root; absolute paths to system files; symlinks that resolve outside the roots; misconfigured userRoot (e.g. entry resolved to the wrong directory).

Related errors


AI-assisted analysis of slidevjs/slidev@0d798ace58 (2026-08-12). Data as JSON: /api/errors/0617793798a640b3. Report an issue: GitHub.