affaan-m/ECC · error · Error
Project memory .gitignore does not contain the required fail
Error message
Project memory .gitignore does not contain the required fail-closed rules.
What it means
When saving to the project scope, ensureProjectScopeIgnored creates a .gitignore whose exact contents are '*\n!.gitignore\n' (ignore everything, fail-closed). If a .gitignore already exists at the project memory root and its contents do not byte-match that required string, the vault refuses to proceed rather than silently widening what gets committed. This prevents an existing permissive .gitignore from leaking memory files into git.
Source
Thrown at scripts/lib/memory-vault.js:257
if (operationError) throw operationError;
if (cleanupError) throw cleanupError;
}
function ensureProjectScopeIgnored(roots, scope) {
if (scope !== 'project') return;
const root = roots.project;
const ignorePath = path.join(root, '.gitignore');
try {
writeCreateOnlyTextFile(ignorePath, PROJECT_MEMORY_GITIGNORE, root);
} catch (error) {
if (!error || error.code !== 'EEXIST') throw error;
const existing = readRegularTextFile(ignorePath, {
label: 'project memory .gitignore',
maxBytes: MAX_DOCUMENT_BYTES,
trustedRoot: root,
});
if (existing !== PROJECT_MEMORY_GITIGNORE) {
throw new Error(
'Project memory .gitignore does not contain the required fail-closed rules.'
);
}
}
}
function normalizeScopes(scopes = MEMORY_SCOPES) {
const values = Array.isArray(scopes) ? scopes : [scopes];
return uniqueStrings(values, {
label: 'scopes',
limit: MEMORY_SCOPES.length,
validator: value => validateEnum(value, MEMORY_SCOPES, 'memory scope'),
});
}
function initializeVault(options = {}) {
const roots = options.roots || resolveVaultRoots(options);
const scopes = normalizeScopes(options.scopes || DEFAULT_RECALL_SCOPES);View on GitHub (pinned to 01e15490f0)
Solutions
- Open <projectRoot>/.ecc/memory/project/.gitignore and replace its contents with exactly two lines: '*' and '!.gitignore' (with a trailing newline, LF not CRLF).
- If you intentionally want to track certain memory files, that intent is incompatible with the project scope's fail-closed policy — store those memories in the user or team scope instead.
- Delete the .gitignore and let the vault recreate it on the next save: rm <root>/.gitignore, then re-run saveMemory.
- Run git config core.autocrlf false (or add a .gitattributes) so git does not rewrite the file's line endings after creation.
Example fix
# before: .ecc/memory/project/.gitignore contains '*.md\n!important.md\n' # replace with the fail-closed policy exactly printf '*\n!.gitignore\n' > .ecc/memory/project/.gitignore # after: contents are exactly '*\n!.gitignore\n'
Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
const path = require('path');
const REQUIRED = '*\n!.gitignore\n';
function assertGitignoreCorrect(projectRoot) {
const p = path.join(projectRoot, '.ecc', 'memory', 'project', '.gitignore');
if (fs.existsSync(p) && fs.readFileSync(p, 'utf8') !== REQUIRED) {
throw new Error('Project memory .gitignore does not contain the required fail-closed rules.');
}
} Try / catch
try { saveMemory({ ...input, scope: 'project' }); }
catch (error) {
if (/fail-closed rules/.test(error.message)) {
console.error('Fix .ecc/memory/project/.gitignore to be exactly:*\\n!.gitignore\\n');
return;
}
throw error;
} Prevention
- Never hand-edit .ecc/memory/project/.gitignore — let the vault create it.
- Add a .gitattributes forcing LF for that path so git does not rewrite line endings.
- If you need certain memories tracked, use the user or team scope, not project.
- After cloning, run initializeVault before saving to let it create the .gitignore.
When it happens
Trigger: Calling saveMemory (or initializeVault) with scope 'project' when <projectRoot>/.ecc/memory/project/.gitignore already exists with any content other than exactly '*\n!.gitignore\n'. Triggered by a user hand-creating a .gitignore, a previous vault version writing a different format, or an editor adding a trailing newline.
Common situations: A user manually authored a .gitignore to keep certain memories tracked; a CI step wrote its own ignore rules; line-ending normalization (CRLF) on Windows changed the byte content; a git hook or template injected content; a prior ECC version used a different ignore string.
Related errors
- ${label} must not contain control or bidirectional formattin
- memory body must not contain unsafe control or bidirectional
- No trusted boundary policy is configured for memory scope "$
- ${label} must be a regular, non-symlink file.
- Refusing to save memory containing a suspected secret (${sec
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/34647dcb4dbbbd16.
Report an issue: GitHub.