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

  1. Open <projectRoot>/.ecc/memory/project/.gitignore and replace its contents with exactly two lines: '*' and '!.gitignore' (with a trailing newline, LF not CRLF).
  2. 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.
  3. Delete the .gitignore and let the vault recreate it on the next save: rm <root>/.gitignore, then re-run saveMemory.
  4. 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

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


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/34647dcb4dbbbd16. Report an issue: GitHub.