paperclipai/paperclip · error
Could not replace navigation
Error message
Could not replace ${provider} ${tab} navigation What it means
`patchDesktopNavigation` in the v7 generator post-processes base SVGs by locating a fixed navigation group (`<g><text x="264" y="94"` through `</g><text x="504"`) and swapping in a provider/tab-specific endpoint nav. The hardcoded geometric anchors assume exact base-SVG layout; if either anchor is absent, the script throws rather than emitting a screen with the wrong navigation.
Solutions
- Update the start/end anchor strings in `patchDesktopNavigation` to match the current base-SVG markup (check a generated/base SVG for the actual text coordinates).
- If coordinates are now dynamic, compute the anchors from the same constants used to draw the nav instead of hardcoding x=264/y=94.
- Ensure the base SVG drawing function still emits the nav group the patcher expects (e.g. after a layout refactor, restore or adapt the marker).
- Re-run the generator for all providers/tabs to confirm no screen trips the patch.
Example fix
// before
const start = svg.indexOf('<g><text x="264" y="94"');
const marker = '</g><text x="504"';
// after (match new base layout)
const start = svg.indexOf('<g><text x="280" y="94"');
const marker = '</g><text x="520"'; Defensive patterns
Strategy: validation
Validate before calling
const anchorsOk = (svg) =>
svg.includes('<g><text x="264" y="94"') && svg.includes('</g><text x="504"');
if (!anchorsOk(baseSvg)) throw new Error("base SVG nav anchors drifted; update patchDesktopNavigation"); Type guard
const patchable = (svg, startToken, endToken) => svg.indexOf(startToken) >= 0 && svg.indexOf(endToken, svg.indexOf(startToken)) >= 0;
Try / catch
try {
svg = patchDesktopNavigation(svg, provider, tab);
} catch (err) {
if (String(err.message).startsWith("Could not replace")) {
console.error(`Base SVG layout for ${provider}/${tab} changed; update the anchors in patchDesktopNavigation.`);
} else throw err;
} Prevention
- Derive nav coordinates in the SVG emitter and the patcher from shared constants.
- Regenerate and inspect one base SVG after any layout/typography change.
- Fail fast in the emitter if the expected nav group is not produced.
When it happens
Trigger: Running the v7 generator after the base SVG template changed its left-nav markup or coordinates (moved text x/y, restructured groups, different tag ordering), so `svg.indexOf` returns -1 for the start anchor or the `</g><text x="504"` end marker.
Common situations: Editing the shared SVG drawing helpers (spacing, font, nav labels) and shifting coordinates; adding screens whose base layout differs; refactoring SVG builders to emit `<text>` before `<g>` or different attribute order.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Could not replace conversation boundary
- Could not load wireframe viewer styles
- Could not load wireframe viewer styles
- Could not load wireframe viewer styles
- Could not load wireframe viewer styles
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/50c037889ca73797.
Report an issue: GitHub.
Appendix: source
Thrown at doc/plans/chat-adapters/generate-wireframes-v7.mjs:184
for (const section of screen.sections) {
const rendered = sectionMobile(section, top);
body.push(rendered.body);
regions.push(rendered.region);
top += rendered.height + 16;
}
const actionY = top + 16;
body.push(button(16, actionY, 343, "Save changes", true));
regions.push({ x:8, y:actionY - 8, w:359, h:64 });
const height = Math.max(812, actionY + 88);
body.push(annotations(regions, true));
return { width:375, height, svg:baseSvg(375, height, body.join("\n")) };
}
function patchDesktopNavigation(svg, provider, tab) {
const start = svg.indexOf('<g><text x="264" y="94"');
const marker = '</g><text x="504"';
const end = svg.indexOf(marker, start);
if (start < 0 || end < 0) throw new Error(`Could not replace ${provider} ${tab} navigation`);
const height = Number(svg.match(/<svg[^>]+height="(\d+)"/)?.[1]);
return `${svg.slice(0, start)}${endpointNav(provider, tab, height)}${svg.slice(end + 4)}`;
}
const conversationBoundaryCopy = {
Slack: "A channel root and its replies are one task. The first mention in an existing thread binds it. A DM has one open task; after completion the next message starts another.",
GitHub: "An issue, pull-request conversation, or inline review thread binds once to one Paperclip task.",
"Microsoft Teams": "A channel post and its replies are one task. A personal or group chat has one open task; after completion the next message starts another.",
Telegram: "A DM or ordinary group has one open task; after completion the next addressed message starts another. A forum topic has one stable topic-to-task binding."
};
function patchConversationBoundary(svg, provider, mobile) {
const startToken = mobile ? '<text x="16" y="260"' : '<text x="504" y="234"';
const endToken = mobile ? '<line x1="16" y1="348"' : '<line x1="504" y1="278"';
const start = svg.indexOf(startToken);
const end = svg.indexOf(endToken, start);
if (start < 0 || end < 0) throw new Error(`Could not replace ${provider} conversation boundary`);
const copy = conversationBoundaryCopy[provider];View on GitHub (pinned to 3f1d897a7c)