nexu-io/open-design · error · Error
${RESOURCE_ROOT_ENV} must be under the workspace root or app
Error message
${RESOURCE_ROOT_ENV} must be under the workspace root or app resources path What it means
Thrown by resolveDaemonResourceRoot() when OD_RESOURCE_ROOT is set but the resolved path is not inside any of the supplied safeBases (workspace root or app resources path). It is a path-traversal / escape guard: the daemon only serves resources from sanctioned roots, so a configured resource root outside them is rejected hard. Returns null when the env var is unset (the legitimate 'no override' case).
Source
Thrown at apps/daemon/src/daemon-paths.ts:83
export interface ResolveDaemonResourceRootOptions {
configured?: string;
safeBases?: Array<string | null | undefined>;
}
export function resolveDaemonResourceRoot({
configured = process.env[RESOURCE_ROOT_ENV],
safeBases,
}: ResolveDaemonResourceRootOptions = {}): string | null {
if (!configured || configured.length === 0) return null;
const resolved = path.resolve(configured);
const normalizedSafeBases = (safeBases ?? [])
.filter((base): base is string => typeof base === 'string' && base.length > 0)
.map((base) => path.resolve(base));
if (!normalizedSafeBases.some((base) => isPathWithin(base, resolved))) {
throw new Error(
`${RESOURCE_ROOT_ENV} must be under the workspace root or app resources path`,
);
}
return resolved;
}
export function resolveDaemonResourceDir(
resourceRoot: string | null,
segment: string,
fallback: string,
): string {
return resourceRoot ? path.join(resourceRoot, segment) : fallback;
}
export interface ResolveDaemonPluginPreviewsDirOptions {
env?: NodeJS.ProcessEnv;
resourceRoot: string | null | undefined;View on GitHub (pinned to 5be4028344)
Solutions
- Set OD_RESOURCE_ROOT to a directory inside the workspace root or the app resources path.
- Unset OD_RESOURCE_ROOT to let the daemon derive resources from the workspace / packaged resources path.
- If a symlink is involved, resolve it to its real target and ensure that target is inside a safe base.
Example fix
# before: pointing outside the workspace export OD_RESOURCE_ROOT=/opt/my-resources # after: keep it inside the workspace (or unset) export OD_RESOURCE_ROOT=$PWD/resources # or simply: unset OD_RESOURCE_ROOT
Defensive patterns
Strategy: validation
Validate before calling
function assertResourceRootInSafeBase(configured: string | undefined, safeBases: string[]) {
if (!configured) return null;
const resolved = path.resolve(configured);
if (!safeBases.some(b => isPathWithin(b, resolved))) {
throw new Error('OD_RESOURCE_ROOT must be under workspace or app resources path');
}
return resolved;
} Type guard
function isWithinAnyBase(target: string, bases: string[]): boolean {
return bases.some(b => isPathWithin(b, target));
} Try / catch
try { resolveDaemonResourceRoot({ safeBases }); }
catch (e) {
if (e instanceof Error && /OD_RESOURCE_ROOT/.test(e.message)) {
// unset OD_RESOURCE_ROOT and fall back to derived resources path
} else throw e;
} Prevention
- Only set OD_RESOURCE_ROOT to a dir inside the workspace root or packaged resources path.
- Resolve symlinks before configuring OD_RESOURCE_ROOT.
- Leave it unset in dev unless you need a specific resources override.
When it happens
Trigger: Setting OD_RESOURCE_ROOT to an absolute path outside the workspace root or the packaged app resources path; using a relative path that resolves outside the safe bases via '..'; pointing at a symlink whose target escapes the bases.
Common situations: Operator pointed OD_RESOURCE_ROOT at /etc or an arbitrary absolute dir; a packaged run inherited a dev-time OD_RESOURCE_ROOT pointing outside Contents/Resources; a symlinked resources dir resolves outside the safe base.
Related errors
- OD_DATA_DIR is required when OD_SANDBOX_MODE is enabled
- invalid brand id: ${input.brandId}
- invalid design system id: ${designSystemId}
- OD_DATA_DIR "${resolved}" is not writable: ${e.message} Curr
- invalid zip file name
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/84a9ac2515f9c1d2.
Report an issue: GitHub.