n8n-io/n8n · error · Error

Access denied: cannot access "${relativePath}"

Error message

Access denied: cannot access "${relativePath}"

What it means

Thrown by resolveSafePathDetails() when isProtectedSettingsPath() returns true for the fully resolved real path. This is a secondary security guard that runs after the base-directory containment check — it catches cases where a symlink resolves to a protected path (e.g. the n8n settings/config directory) even if that path is technically inside the base dir. The check uses the resolved real path, so symlink-based bypasses targeting n8n's internal configuration are caught.

Source

Thrown at packages/@n8n/computer-use/src/tools/filesystem/fs-utils.ts:250

				}
			} catch {
				// lstat also failed — the path truly does not exist.
			}

			// Path does not exist and is not a symlink; append remaining parts as-is.
			current = path.join(current, ...parts.slice(i));
			break;
		}
	}

	if (!current.startsWith(realBase + path.sep) && current !== realBase) {
		throw new Error(`Path "${relativePath}" escapes the base directory`);
	}

	// Check if the resolved real path targets a protected path (e.g. settings directory).
	// This catches symlink-based bypasses since `current` has all symlinks resolved.
	if (isProtectedSettingsPath(current)) {
		throw new Error(`Access denied: cannot access "${relativePath}"`);
	}

	return { absolutePath: absolute, realBasePath: realBase, resolvedPath: current };
}

export async function resolveSafePath(basePath: string, relativePath: string): Promise<string> {
	const { absolutePath } = await resolveSafePathDetails(basePath, relativePath);
	return absolutePath;
}

export async function resolveReadablePath(basePath: string, relativePath: string): Promise<string> {
	const { absolutePath, realBasePath, resolvedPath } = await resolveSafePathDetails(
		basePath,
		relativePath,
	);
	assertNoExcludedSegments(absolutePath, basePath);
	assertNoExcludedSegments(resolvedPath, realBasePath);
	return absolutePath;

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Remove the symlink that targets the protected settings path
  2. Access n8n settings through the n8n UI or REST API, not through filesystem tools
  3. Audit symlinks in the base directory for any that resolve to system or application config paths
Defensive patterns

Strategy: validation

Validate before calling

import { isProtectedSettingsPath } from '../config';

async function isSafeToAccess(realPath: string): Promise<boolean> {
  return !isProtectedSettingsPath(realPath);
}

Type guard

function isProtectedPathError(e: unknown): boolean {
  return e instanceof Error && e.message.startsWith('Access denied: cannot access');
}

Prevention

When it happens

Trigger: A symlink inside the base directory points to the n8n settings or configuration directory, and a tool tries to read or write through that symlink. The real-path check after incremental symlink resolution detects the protected target.

Common situations: Malicious or accidental symlink that would expose n8n's internal configuration, encryption keys, or settings files through the filesystem tools.

Understand the failure class

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/0bea1289cab6fb1a. Report an issue: GitHub.