paperclipai/paperclip · error · Error

Wiki page path must be a markdown file: ${path}

Error message

Wiki page path must be a markdown file: ${path}

What it means

Thrown by assertPagePath when the normalized path passes the location check but does not end with '.md'. Enforces that page writes are markdown only.

Source

Thrown at packages/plugins/plugin-llm-wiki/src/wiki/core.ts:1188

    trimmed !== "IDEA.md" &&
    trimmed !== "index.md" &&
    trimmed !== "log.md" &&
    !trimmed.startsWith("raw/") &&
    !trimmed.startsWith("wiki/") &&
    !(options.allowMetadata && trimmed.startsWith(".paperclip/"))
  ) {
    throw new Error(`Wiki path must stay inside AGENTS.md, IDEA.md, raw/, or wiki/: ${path}`);
  }
  return trimmed;
}

function assertPagePath(path: string): string {
  const normalized = assertWikiPath(path);
  if (normalized !== "index.md" && normalized !== "log.md" && normalized !== "WIKI.md" && normalized !== "AGENTS.md" && normalized !== "IDEA.md" && !normalized.startsWith("wiki/")) {
    throw new Error(`Wiki page writes must target AGENTS.md, IDEA.md, or wiki/: ${path}`);
  }
  if (!normalized.endsWith(".md")) {
    throw new Error(`Wiki page path must be a markdown file: ${path}`);
  }
  return normalized;
}

function assertPageWriteAllowed(path: string, writer: WritePageInput["writer"] = "agent_tool"): void {
  if (writer !== "board_ui" && PROTECTED_WIKI_CONTROL_FILES.has(path)) {
    throw new Error(`Refusing to overwrite protected wiki control file ${path}; board-managed edits must use the wiki UI.`);
  }
}

function assertRawPath(path: string): string {
  const normalized = assertWikiPath(path);
  if (!normalized.startsWith("raw/")) {
    throw new Error(`Source path must stay inside raw/: ${path}`);
  }
  return normalized;
}

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Append '.md' to the filename before calling writePage.
  2. Route non-markdown content to the raw-source API under raw/ instead.
  3. Validate the extension at the form boundary.

Example fix

// before
await writePage(ctx, { companyId, path: "wiki/notes" });

// after
await writePage(ctx, { companyId, path: "wiki/notes.md" });
Defensive patterns

Strategy: validation

Validate before calling

function ensureMarkdownExtension(path) {
  const t = String(path).trim().replace(/^\/+/, "");
  if (!t.endsWith(".md")) throw new Error(`Wiki page path must be a markdown file: ${path}`);
  return t;
}

Type guard

function isMarkdownPath(path) {
  return String(path ?? "").trim().replace(/^\/+/, "").endsWith(".md");
}

Try / catch

try {
  await writePage(ctx, { companyId, path, content });
} catch (err) {
  if (/must be a markdown file/.test(err.message)) {
    return writePage(ctx, { companyId, path: `${path}.md`, content });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling writePage with a path like 'wiki/notes.txt', 'wiki/index', or 'wiki/data.json'.

Common situations: Forgetting the extension; importing a non-markdown file via the page API; auto-generated path missing its suffix.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/16ed8d9304f39975. Report an issue: GitHub.