affaan-m/ECC · error · Error

Memory document must start with --- frontmatter.

Error message

Memory document ${sourcePath} must start with --- frontmatter.

What it means

parseMemoryDocument() requires a memory document to begin with the `---` frontmatter opening marker (for string sources; buffer sources bypass this check). The frontmatter-delimited format is the document's structural contract, so a file missing the opening fence is rejected before any field parsing.

Solutions

  1. Start the document with `---` on the very first line, followed by frontmatter and a closing `---`.
  2. Remove any leading blank lines, BOM, or preamble text before the opening fence.
  3. Ensure the fence is exactly three dashes on its own line (no leading spaces, no extra characters).
  4. If the file isn't a memory document, move it out of the memory vault / don't pass it to parseMemoryDocument.

Example fix

// before (missing fences)
id: "notes"
title: "Deploy notes"

// after
---
id: "notes"
title: "Deploy notes"
---
Body text here.
Defensive patterns

Strategy: validation

Validate before calling

const src = fs.readFileSync(path, 'utf8').replace(/^\uFEFF/, '');
if (!/^---(\r?\n|$)/.test(src)) throw new Error(`${path} is missing the opening --- frontmatter fence`);

Type guard

const hasFrontmatter = (s) => typeof s === 'string' && /^---\r?\n/.test(s.replace(/^\uFEFF/, ''));

Try / catch

try {
  parseMemoryDocument(source, path);
} catch (err) {
  if (err.message.includes('must start with --- frontmatter')) {
    console.error('%s lacks the leading --- fence; add frontmatter or move the file out of the vault.', path);
  } else throw err;
}

Prevention

When it happens

Trigger: Loading a memory file that starts with blank lines, a BOM, prose/markdown text, a different fence style (e.g. `--- ` with trailing content on the same line, `---\r` handled ok but `----` not), or a completely empty file; passing a string that was stripped of frontmatter.

Common situations: Creating a memory file by hand and forgetting the --- fences; a formatter or linter stripping/altering the leading fence; writing notes without frontmatter into the vault directory and expecting them to load; file begins with a UTF-8 BOM that breaks the ^--- regex match.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at scripts/lib/memory-vault-format.js:248

    throw new Error(`Unknown memory frontmatter field in ${sourcePath}.`);
  }
  if (seen.has(objectKey)) {
    throw new Error(`Duplicate memory frontmatter field in ${sourcePath}.`);
  }
  const rawValue = line.slice(separator + 1).trim();
  try {
    return { objectKey, value: JSON.parse(rawValue) };
  } catch {
    throw new Error(`Memory frontmatter field in ${sourcePath} must use a JSON value.`);
  }
}

function parseMemoryDocument(source, sourcePath = '<memory>') {
  const openingMarker = typeof source === 'string'
    ? /^---\r?\n/.exec(source)
    : null;
  if (!openingMarker) {
    throw new Error(`Memory document ${sourcePath} must start with --- frontmatter.`);
  }
  if (Buffer.byteLength(source, 'utf8') > MAX_DOCUMENT_BYTES) {
    throw new Error(`Memory document ${sourcePath} is too large.`);
  }

  const frontmatterStart = openingMarker[0].length;
  const remainder = source.slice(frontmatterStart);
  const closingMarker = /\r?\n---(?=\r?\n|$)/.exec(remainder);
  if (!closingMarker) {
    throw new Error(`Memory document ${sourcePath} has no closing frontmatter marker.`);
  }

  const frontmatterSource = remainder.slice(0, closingMarker.index);
  const parsed = frontmatterSource.split(/\r?\n/).reduce((state, line) => {
    const next = parseFrontmatterLine(line, sourcePath, state.seen);
    return {
      values: { ...state.values, [next.objectKey]: next.value },
      seen: new Set([...state.seen, next.objectKey]),

View on GitHub (pinned to 8321021c54)