microsoft/playwright · error · Error

Trace entry '${entry}' escapes output directory

Error message

Trace entry '${entry}' escapes output directory

What it means

Thrown by extractTrace() when a zip entry inside a .trace.zip file would, when joined with the output directory, resolve to a path outside that directory (resolveWithinRoot returns null). This is a path-traversal / 'zip-slip' guard: a maliciously or corruptly crafted trace archive could otherwise write files anywhere on disk during extraction.

Source

Thrown at packages/playwright-core/src/tools/trace/traceParser.ts:91

    const resolved = resolveWithinRoot(this._dir, entryName);
    if (!resolved)
      return;
    try {
      const buffer = await fs.promises.readFile(resolved);
      return new Blob([new Uint8Array(buffer)]);
    } catch {
    }
  }
}

export async function extractTrace(traceFile: string, outDir: string): Promise<void> {
  const zipFile = new ZipFile(traceFile);
  try {
    const entries = await zipFile.entries();
    for (const entry of entries) {
      const outPath = resolveWithinRoot(outDir, entry);
      if (!outPath)
        throw new Error(`Trace entry '${entry}' escapes output directory`);
      await fs.promises.mkdir(path.dirname(outPath), { recursive: true });
      const buffer = await zipFile.read(entry);
      await fs.promises.writeFile(outPath, buffer);
    }
  } finally {
    zipFile.close();
  }
}

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Re-export the trace from the original Playwright run; do not use a hand-modified archive.
  2. If you received the trace from an untrusted source, treat it as untrusted and do not extract it.
  3. Inspect the archive entry names (unzip -l) for '../' or leading slashes and repackage it without traversal sequences if you control the source.

Example fix

// inspect offending entries
unzip -l trace.zip | grep '\.\./'
// regenerate from source
npx playwright test --trace=on
Defensive patterns

Strategy: validation

Validate before calling

import path from 'path';
function entriesAreSafe(entries: string[], outDir: string): boolean {
  const root = path.resolve(outDir);
  return entries.every(e => {
    if (path.isAbsolute(e)) return false;
    const resolved = path.resolve(root, e);
    return resolved === root || resolved.startsWith(root + path.sep);
  });
}

Type guard

function isSafeEntryName(entry: string): boolean {
  return !path.isAbsolute(entry) && !entry.includes('..');
}

Try / catch

try {
  await extractTrace(traceFile, outDir);
} catch (e) {
  if (/escapes output directory/.test((e as Error).message)) {
    console.warn('Refusing to extract unsafe trace archive:', traceFile);
  }
  throw e;
}

Prevention

When it happens

Trigger: Opening/extracting a trace archive whose internal entry names contain '../' sequences or absolute paths, e.g. an entry named '../../../etc/passwd'. Can also occur with archivers that emit absolute entry names.

Common situations: A hand-edited or third-party-supplied trace zip; a corrupted download; an archive produced by a tool that stores absolute paths. In normal Playwright-generated traces this should never happen.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/522a1ad4b046034a. Report an issue: GitHub.