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

  1. 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).
  2. If coordinates are now dynamic, compute the anchors from the same constants used to draw the nav instead of hardcoding x=264/y=94.
  3. 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).
  4. 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

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


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)