paperclipai/paperclip · error · Error
Wiki path must stay inside AGENTS.md, IDEA.md, raw/, or wiki
Error message
Wiki path must stay inside AGENTS.md, IDEA.md, raw/, or wiki/: ${path} What it means
Thrown by assertWikiPath when a path passes the structural check but is not one of the allowed roots: .gitignore, WIKI.md, AGENTS.md, IDEA.md, index.md, log.md, a raw/ prefixed path, a wiki/ prefixed path, or (only with allowMetadata) a .paperclip/ prefixed path. Enforces the wiki's controlled directory layout.
Source
Thrown at packages/plugins/plugin-llm-wiki/src/wiki/core.ts:1177
if (
!trimmed ||
trimmed.includes("\\") ||
trimmed.split("/").some((segment) => segment === "" || segment === "." || segment === "..")
) {
throw new Error(`Invalid wiki path: ${path}`);
}
if (
trimmed !== ".gitignore" &&
trimmed !== "WIKI.md" &&
trimmed !== "AGENTS.md" &&
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.`);View on GitHub (pinned to 67001ec6eb)
Solutions
- Place page content under wiki/ and raw assets under raw/.
- Use only the recognized control files (AGENTS.md, IDEA.md, WIKI.md, index.md, log.md) for their intended purpose.
- If you need .paperclip/ metadata access, ensure the call path passes { allowMetadata: true } to assertWikiPath (internal use only).
- Reject unrecognized top-level filenames at the UI boundary.
Example fix
// before
await writePage(ctx, { companyId, path: "notes/meeting.md" });
// after
await writePage(ctx, { companyId, path: "wiki/notes/meeting.md" }); Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED_TOP = new Set([".gitignore", "WIKI.md", "AGENTS.md", "IDEA.md", "index.md", "log.md"]);
function isAllowedWikiRoot(trimmed) {
return ALLOWED_TOP.has(trimmed) || trimmed.startsWith("raw/") || trimmed.startsWith("wiki/") || trimmed.startsWith(".paperclip/");
}
function sanitizeWikiRoot(path) {
const trimmed = String(path).trim().replace(/^\/+/, "");
if (!isAllowedWikiRoot(trimmed)) throw new Error(`Wiki path must stay inside AGENTS.md, IDEA.md, raw/, or wiki/: ${path}`);
return trimmed;
} Type guard
function isAllowedWikiPath(path) {
const t = String(path ?? "").trim().replace(/^\/+/, "");
return isAllowedWikiRoot(t);
} Try / catch
try {
await writePage(ctx, { companyId, path, content });
} catch (err) {
if (/must stay inside/.test(err.message)) {
return res.status(400).json({ error: "Move the file under wiki/ or raw/, or use a recognized control file." });
}
throw err;
} Prevention
- Default new page paths to wiki/<name>.md.
- Keep raw assets under raw/.
- Reject unrecognized top-level filenames early.
When it happens
Trigger: Submitting a path that lives outside the allowed roots, e.g. 'notes/foo.md', 'config.json', 'README.md', or '.paperclip/x' without allowMetadata.
Common situations: Assuming any markdown file is writable; writing to a top-level filename not in the allowlist; trying to use .paperclip/ metadata paths from a context that did not opt in.
Related errors
- Invalid wiki path: ${path}
- Refusing to write export file outside output directory: ${re
- Invalid object key for company ${companyId}.
- sync operation ${label} path is not a confined absolute path
- sync operation ${label} path escapes its confinement root: $
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/819c3ce228e9eecf.
Report an issue: GitHub.