paperclipai/paperclip · error
Expected five documented annotations for screen
Error message
Expected five documented annotations for screen ${screen.id} What it means
Spec-validation guard in the wireframe generator: every screen definition in uiSurfaceSpec must carry exactly five documented annotations (the viewer renders a numbered annotation list per screen). This sentinel throws during generation when a screen's annotations array is missing, empty, or does not have length 5 — it is an authoring-data error in the script's screen spec, not a runtime condition.
Solutions
- Count the annotations array for the screen named in the message and pad or trim it to exactly five entries.
- Check for a recently edited screen spec where annotations were removed or the array was renamed so it resolved to undefined.
- Replace an empty annotations array with five meaningful annotation strings covering the screen's key behaviors.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at doc/plans/chat-adapters/generate-wireframes.mjs:197 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/30e976be0f547875.
Report an issue: GitHub.
Appendix: source
Thrown at doc/plans/chat-adapters/generate-wireframes.mjs:197
["Comments and messaging", "GitHub · Linear · Notion · Telegram", "Token/app + resource allowlist", "Thread and mention rules vary"],
["Phone, social, and email", "WhatsApp · Twilio · X · Resend · iMessage", "Sender identity + webhook", "Media, window, and rate limits vary"],
],
notes: ["Provider taxonomy drives setup fields without cloning the wizard.", "Capability rows name supported, fallback, and unavailable behavior.", "Shared states cover loading, empty, degraded, denied, rate-limited, revoked, and dead letter."],
},
];
const uiSurfaceSpec = readFileSync(join(root, "2026-09-03-chat-adapters-ui-surfaces.md"), "utf8");
const annotationMap = new Map(
[...uiSurfaceSpec.matchAll(/### (\d{2})[^\n]*\n\nPurpose:[^\n]*\n\n((?:\d+\.[^\n]*\n){5})/g)].map((match) => [
match[1],
match[2].trim().split("\n").map((line) => line.replace(/^\d+\.\s*/, "")),
]),
);
for (const screen of screens) {
screen.annotations = annotationMap.get(screen.id);
if (!screen.annotations || screen.annotations.length !== 5) {
throw new Error(`Expected five documented annotations for screen ${screen.id}`);
}
}
const esc = (value) => String(value)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """);
const text = (x, y, value, size = 14, fill = "#000", extra = "") =>
`<text x="${x}" y="${y}" font-size="${size}" stroke="none" fill="${fill}" ${extra}>${esc(value)}</text>`;
const multiline = (x, y, lines, size = 14, fill = "#666", gap = 24) =>
lines.map((line, index) => text(x, y + index * gap, line, size, fill)).join("\n");
function wrapWords(value, maxCharacters = 48) {
const lines = [];
let current = "";View on GitHub (pinned to 3f1d897a7c)