affaan-m/ECC · critical · Error

Refusing to save memory containing a suspected secret

Error message

Refusing to save memory containing a suspected secret (${secretKinds.join(', ')}).

What it means

saveMemory serializes the memory payload to JSON and runs a secret scanner (findPotentialSecrets) before writing. If the payload matches patterns for API keys, tokens, passwords, or similar, the save is refused outright — a fail-closed guard so secrets never land in memory files that might be synced or committed.

Solutions

  1. Remove the secret from the memory content; store a reference/pointer instead of the credential.
  2. Redact the sensitive value (e.g. 'sk-***') before saving.
  3. Store the secret in a proper secret manager and save only its identifier in the memory.
  4. If it is a false positive, rephrase the content so it no longer matches the secret patterns.

Example fix

// before
saveMemory({ id: 'deploy-note', content: 'use API_KEY=sk-live-abc123' });
// after
saveMemory({ id: 'deploy-note', content: 'use API_KEY from secret manager (entry: deploy/api-key)' });
Defensive patterns

Strategy: validation

Validate before calling

const SECRET_PATTERNS = [/sk-[A-Za-z0-9]{20,}/, /AKIA[0-9A-Z]{16}/, /-----BEGIN [A-Z ]*PRIVATE KEY-----/, /Bearer\s+[A-Za-z0-9._\-]{20,}/, /(?:password|passwd|secret|token)\s*[=:]\s*\S+/i];
const found = SECRET_PATTERNS.filter(re => re.test(JSON.stringify(memory)));
if (found.length) throw new Error('Memory content contains a suspected secret; redact before saving.');

Try / catch

try {
  saveMemory(memory);
} catch (err) {
  if (err.message.startsWith('Refusing to save memory containing a suspected secret')) {
    // redact the matched content and retry
  } else throw err;
}

Prevention

When it happens

Trigger: Calling saveMemory (directly or via runWriteCommand/saveWithId) with any field whose stringified content matches a secret heuristic: e.g. a value like 'sk-...', 'Bearer <token>', 'password=...', PEM keys, AWS keys.

Common situations: Saving a memory that quotes an .env snippet or API key for later reference; a code snippet embedded in the memory contains a hardcoded credential; CI tokens accidentally pasted into notes.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/76d1d93fd780ccdd. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/memory-vault.js:324

    scope: input.scope || 'project',
    trust: 'unreviewed',
    status: 'active',
    sourceHarness: input.sourceHarness || 'unknown',
    targetHarnesses: input.targetHarnesses || ['all'],
    tags: input.tags || [],
    links: input.links || [],
    createdAt: now,
    updatedAt: now,
    body: input.body || '',
  });
}

function saveMemory(input, options = {}) {
  const roots = options.roots || resolveVaultRoots(options);
  const memory = normalizeSaveInput(input || {}, options);
  const secretKinds = findPotentialSecrets(JSON.stringify(memory));
  if (secretKinds.length > 0) {
    throw new Error(`Refusing to save memory containing a suspected secret (${secretKinds.join(', ')}).`);
  }

  const root = assertMemoryRootSafe(roots, memory.scope);
  fs.mkdirSync(root, { recursive: true, mode: 0o700 });
  ensureProjectScopeIgnored(roots, memory.scope);
  const directory = path.join(root, `${memory.kind}s`);
  assertMemoryDirectorySafe(directory, root);
  fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
  const destination = path.join(directory, `${memory.id}.md`);

  try {
    writeCreateOnlyTextFile(destination, serializeMemoryDocument(memory), root);
  } catch (error) {
    if (error && error.code === 'EEXIST') {
      throw new Error(`Memory ${memory.id} already exists; writes are create-only.`);
    }
    throw error;
  }

View on GitHub (pinned to 8321021c54)