mastra-ai/mastra · error · Error

Experiment worker artifacts cannot contain escaping symlinks

Error message

Experiment worker artifacts cannot contain escaping symlinks: ${artifactPath}

What it means

During artifact digesting, a symlink whose relative target resolves outside the artifact root (relative(root, resolved) escapes with '..' or lands on an absolute path) is rejected. Such links would make the unpacked artifact read or point to files outside its own tree — a security and reproducibility hazard — so the build aborts naming the offending link.

Source

Thrown at packages/cli/src/commands/experiment/ExperimentBundler.ts:277

      const stats = await lstat(fullPath);
      if (stats.isDirectory()) {
        await visit(fullPath);
      } else if (stats.isFile()) {
        files.push({
          path: artifactPath,
          sha256: createHash('sha256')
            .update(await readFile(fullPath))
            .digest('hex'),
        });
      } else if (stats.isSymbolicLink()) {
        const target = await readlink(fullPath);
        if (isAbsolute(target)) {
          throw new Error(`Experiment worker artifacts cannot contain absolute symlinks: ${artifactPath}`);
        }
        const resolvedTarget = resolve(dirname(fullPath), target);
        const artifactTarget = relative(root, resolvedTarget);
        if (artifactTarget === '..' || artifactTarget.startsWith(`..${sep}`) || isAbsolute(artifactTarget)) {
          throw new Error(`Experiment worker artifacts cannot contain escaping symlinks: ${artifactPath}`);
        }
        files.push({
          path: artifactPath,
          type: 'symlink',
          target,
          sha256: createHash('sha256').update(target).digest('hex'),
        });
      } else {
        throw new Error(`Unsupported artifact file type: ${artifactPath}`);
      }
    }
  };
  await visit(root);
  return files;
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Locate the listed symlink and retarget it to a path inside the artifact (or replace it with a real copy).
  2. Bundle the linked dependency's contents instead of linking (e.g. rely on the bundler's dynamicPackages/externals handling rather than filesystem links).
  3. If it comes from a dependency's install script, patch or vendor the dependency.
  4. Clean the output directory and rebuild to remove stale links.

Example fix

// before
ln -s ../../shared/transforms ./transforms
// after
cp -r ../../shared/transforms ./transforms   # real copy inside the artifact
Defensive patterns

Strategy: validation

Validate before calling

import { lstat, readlink, readdir } from 'node:fs/promises';
import { resolve, dirname, relative, isAbsolute, sep, join } from 'node:path';
async function assertNoEscapingSymlinks(root: string, dir = root) {
  for (const e of await readdir(dir, { withFileTypes: true })) {
    const p = join(dir, e.name);
    if (e.isDirectory()) await assertNoEscapingSymlinks(root, p);
    else if (e.isSymbolicLink()) {
      const t = relative(root, resolve(dirname(p), await readlink(p)));
      if (t === '..' || t.startsWith(`..${sep}`) || isAbsolute(t)) throw new Error(`escaping symlink: ${p}`);
    }
  }
}

Type guard

null

Try / catch

try {
  await buildExperimentWorker({ outputDir });
} catch (e) {
  if (String(e?.message).includes('escaping symlinks')) {
    console.error('Retarget the listed symlink inside the artifact or copy the files in');
  } else throw e;
}

Prevention

When it happens

Trigger: collectFileDigests finds a symlink in the output directory whose target, resolved against the link's directory, escapes the artifact root, e.g. `../../../etc/something` or `../../shared-lib`.

Common situations: Monorepo setups where packages symlink to sibling workspace packages outside the output; vendored code linking to a shared assets dir; pnpm-style virtual-store links escaping the bundle; copying source trees that contain parent-relative links.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/57b0cedea6563aa1. Report an issue: GitHub.