affaan-m/ECC · error · Error
Memory roots must include a trusted boundary policy.
Error message
Memory roots must include a trusted boundary policy.
What it means
Thrown by assertMemoryRootSafe() as its opening guard: the roots argument must be a non-null, non-array object. roots is expected to be the frozen object produced by resolveVaultRoots() — a plain object with 'project', 'team', and 'user' string keys plus a hidden Symbol-keyed boundary map. Passing anything else is a programming error in the caller.
Source
Thrown at scripts/lib/memory-vault.js:99
? realpathNearestExisting(projectVault)
: projectRoot,
team: env.ECC_MEMORY_PROJECT_ROOT
? realpathNearestExisting(projectVault)
: projectRoot,
user: env.ECC_MEMORY_USER_ROOT
? realpathNearestExisting(userVault)
: homeDir,
}),
enumerable: false,
configurable: false,
writable: false,
});
return Object.freeze(roots);
}
function assertMemoryRootSafe(roots, scope) {
if (!roots || typeof roots !== 'object' || Array.isArray(roots)) {
throw new Error('Memory roots must include a trusted boundary policy.');
}
const root = roots[scope];
if (typeof root !== 'string' || root.length === 0) {
throw new Error(`No memory root is configured for scope "${scope}".`);
}
const boundary = roots[VAULT_ROOT_BOUNDARIES]?.[scope];
if (typeof boundary !== 'string' || boundary.length === 0) {
throw new Error(`No trusted boundary policy is configured for memory scope "${scope}".`);
}
assertWithinTrustedRoot(root, boundary, 'access memory through a symlink');
if (fs.existsSync(root) && fs.lstatSync(root).isSymbolicLink()) {
throw new Error(`Refusing to access memory through symlink root: ${root}`);
}
return root;
}
function assertMemoryDirectorySafe(directory, root) {
assertWithinTrustedRoot(directory, root, 'access memory directory');View on GitHub (pinned to 01e15490f0)
Solutions
- Always obtain roots via resolveVaultRoots(options) — never build the object by hand.
- If you must override, call resolveVaultRoots({ ECC_MEMORY_PROJECT_ROOT: '/path' }) with env-style options.
- Pass options through to saveMemory()/searchMemories()/readMemoryFiles() and let them resolve roots themselves.
- Add an assertion in your wrapper: if (!options.roots) options.roots = resolveVaultRoots(options);
Example fix
// before
saveMemory(input, { roots: '/custom/path' }); // string, not a roots object
// after
const roots = resolveVaultRoots({ ECC_MEMORY_PROJECT_ROOT: '/custom/path' });
saveMemory(input, { roots }); Defensive patterns
Strategy: type-guard
Validate before calling
if (!options.roots) options.roots = resolveVaultRoots(options);
if (!options.roots || typeof options.roots !== 'object' || Array.isArray(options.roots)) {
throw new TypeError('options.roots must be a resolveVaultRoots() result');
} Type guard
function isRootsObject(value): value is Record<'project'|'team'|'user', string> {
return value !== null && typeof value === 'object' && !Array.isArray(value)
&& typeof value.project === 'string'
&& typeof value.team === 'string'
&& typeof value.user === 'string';
} Prevention
- Always source roots from resolveVaultRoots() — never hand-build the object.
- In tests, call resolveVaultRoots({ cwd: tempDir }) to get a valid fixture.
- Pass options through to the library APIs and let them resolve roots themselves.
When it happens
Trigger: Calling assertMemoryRootSafe(null, scope), assertMemoryRootSafe(undefined, scope) when options.roots was not set, assertMemoryRootSafe([], scope) when a list of roots was passed by mistake, or a wrapper that built its own roots object without using resolveVaultRoots() and passed a string path instead.
Common situations: Custom wrapper that wanted to override roots but passed a single string. Refactor that changed the shape passed to initializeVault/readMemoryFiles/saveMemory. Test stub that constructed roots manually and forgot the Symbol boundary property.
Related errors
- memory body must be a string.
- memory must be an object.
- No memory root is configured for scope "${scope}".
- No trusted boundary policy is configured for memory scope "$
- ${label} must be a non-empty string.
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/81b3fd6720e47b25.
Report an issue: GitHub.