Skip to content

fix(server): favicon resolution no longer pins the event loop - #5531

Closed
vbb-it wants to merge 1 commit into
pingdotgg:mainfrom
vbb-it:fix/favicon-object-regex-backtracking
Closed

fix(server): favicon resolution no longer pins the event loop#5531
vbb-it wants to merge 1 commit into
pingdotgg:mainfrom
vbb-it:fix/favicon-object-regex-backtracking

Conversation

@vbb-it

@vbb-it vbb-it commented Aug 6, 2026

Copy link
Copy Markdown

What Changed

LINK_ICON_OBJ_RE in ProjectFaviconResolver is now anchored on the literal { and bounded to the enclosing object, with href pulled out of the matched object by a second small pattern.

-const LINK_ICON_OBJ_RE =
-  /(?=[^}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'])(?=[^}]*\bhref\s*:\s*["']([^"'?]+))[^}]*/i;
+const LINK_ICON_OBJ_RE = /\{[^{}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'][^{}]*\}/i;
+const ICON_HREF_RE = /\bhref\s*:\s*["']([^"'?]+)/i;

Three tests come with it. The object branch had no coverage at all — every existing case exercises the <link> branch — so this adds both key orders (rel first, href first with shortcut icon) plus a large brace-sparse source that hangs the suite without the fix.

LINK_ICON_HTML_RE is untouched. It is already anchored on <link\b, so it was never part of the problem.

Why

Fixes #5530.

The old pattern started with a lookahead and no literal anchor, so the engine retried at every offset in the file and rescanned forward with [^}]* from each one. On a brace-sparse file that is quadratic.

That turns one file into a full environment outage. A project with no icon and a large index.html lacking <link rel="icon"> — the shape of a generated single-file build — made resolvePath spin for minutes on the server's only thread. Every endpoint stopped answering, including /.well-known/t3/environment; the desktop client timed out at 10s, respawned the server, and it re-scanned and re-wedged. The environment never recovered on its own.

Anchoring on { restricts start positions to real object literals and bounds each attempt to a single brace group, so the work stays proportional to file size.

Measured on the 1.6 MB generated index.html that triggered this:

before after
1.6 MB icon source, no icon metadata killed at 30s, never completed 2 ms
200 KB ~4s <1 ms

Semantics are preserved — rel before href, href before rel, shortcut icon, and query-string stripping all still resolve.

UI Changes

None.

Verification

  • vp test run src/project/ProjectFaviconResolver.test.ts — 15 passed (12 existing + 3 new), 415 ms.
  • Reverting only the source change and keeping the new tests: the run was still going at 100s and had to be killed, confirming the regression test actually catches this.
  • vp lint on both changed files: clean.
  • tsgo --noEmit for apps/server: exit 0, no diagnostics in the changed files.

Branched off main @ e4abc31f.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes (N/A, no UI change)
  • I included a video for animation/interaction changes (N/A)

Model: Claude Opus 5 · Harness: Claude Code


Note

Medium Risk
Touches core server favicon discovery on every workspace resolve; regex change is localized but incorrect matching could miss icons in nested objects with inner braces.

Overview
Fixes catastrophic slowdown in ProjectFaviconResolver when scanning large sources that lack icon metadata (e.g. generated single-file index.html), which could block the server event loop for minutes.

Object-literal icon detection no longer uses an unanchored lookahead over [^}]*. It now matches only brace-bounded objects that declare rel: "icon" / "shortcut icon", then reads href via a separate ICON_HREF_RE pass so rel/href order stays flexible without rescanning the whole file at every offset.

Tests add first coverage for the object-metadata path (route head links and href-before-rel), plus a large-file timing guard that expects resolvePath to finish in under 5s when no icon is present.

Reviewed by Cursor Bugbot for commit 4b77158. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix favicon resolution in ProjectFaviconResolver to avoid pinning the event loop

  • Replaces the previous unanchored lookahead-based regex (LINK_ICON_OBJ_RE) with an anchored brace-delimited pattern, bounding object-metadata scans to a single {...} block.
  • Introduces ICON_HREF_RE to extract href from the matched object block, supporting any property order.
  • Adds a performance test in ProjectFaviconResolver.test.ts asserting large icon-free sources are scanned in under 5 seconds.
📊 Macroscope summarized 4b77158. 1 file reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

`LINK_ICON_OBJ_RE` was unanchored, so it restarted at every offset in an
icon source file and rescanned forward from each one. A project with no
icon file and a large `index.html` that has no `<link rel="icon">` made
favicon resolution spin for minutes on the server's only thread, so every
connection stopped being answered and the desktop client dropped into a
permanent reconnect loop. Measured on a 1.6 MB generated `index.html`:
200 KB already cost ~4s, and the full file never completed.

Anchor the object pattern on the literal `{` and bound each attempt to the
enclosing object, then pull `href` from the matched object. Both key orders
and `shortcut icon` still resolve, and the same 1.6 MB file now finishes in
2 ms. The object branch had no test coverage, so this adds cases for both
key orders plus a large brace-sparse source that hangs without the fix.

Fixes #5530

Model: Claude Opus 5 · Harness: Claude Code

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7faed43a-6a1c-4e73-b02a-046e98509a6a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the size:XS 0-9 changed lines (additions + deletions). label Aug 6, 2026
/<link\b(?=[^>]*\brel=["'](?:icon|shortcut icon)["'])(?=[^>]*\bhref=["']([^"'?]+))[^>]*>/i;
const LINK_ICON_OBJ_RE =
/(?=[^}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'])(?=[^}]*\bhref\s*:\s*["']([^"'?]+))[^}]*/i;
const LINK_ICON_OBJ_RE = /\{[^{}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'][^{}]*\}/i;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium project/ProjectFaviconResolver.ts:62

LINK_ICON_OBJ_RE uses [^{}]* to bound the match, so any icon metadata object containing nested braces fails to match — for example { attributes: {}, rel: "icon", href: "/favicon.svg" } returns null from extractIconHref instead of /favicon.svg. The prior lookahead-based pattern could scan past inner braces; the new anchored pattern cannot. Consider using a pattern that tolerates nested braces, or strip/normalize brace depth before matching.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/project/ProjectFaviconResolver.ts around line 62:

`LINK_ICON_OBJ_RE` uses `[^{}]*` to bound the match, so any icon metadata object containing nested braces fails to match — for example `{ attributes: {}, rel: "icon", href: "/favicon.svg" }` returns `null` from `extractIconHref` instead of `/favicon.svg`. The prior lookahead-based pattern could scan past inner braces; the new anchored pattern cannot. Consider using a pattern that tolerates nested braces, or strip/normalize brace depth before matching.

/<link\b(?=[^>]*\brel=["'](?:icon|shortcut icon)["'])(?=[^>]*\bhref=["']([^"'?]+))[^>]*>/i;
const LINK_ICON_OBJ_RE =
/(?=[^}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'])(?=[^}]*\bhref\s*:\s*["']([^"'?]+))[^}]*/i;
const LINK_ICON_OBJ_RE = /\{[^{}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'][^{}]*\}/i;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium project/ProjectFaviconResolver.ts:62

LINK_ICON_OBJ_RE now matches the first {...} object containing rel: "icon" even when that object has no href, so extractIconHref never examines later valid declarations. A source like [{ rel: "icon" }, { rel: "icon", href: "/favicon.svg" }] returns null instead of /favicon.svg. The old regex required both rel and href in the same candidate. Consider restoring the href lookahead inside LINK_ICON_OBJ_RE so non-href objects are skipped.

Suggested change
const LINK_ICON_OBJ_RE = /\{[^{}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'][^{}]*\}/i;
const LINK_ICON_OBJ_RE = /\{(?=[^{}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'])(?=[^{}]*\bhref\s*:\s*["']([^"'?]+))[^{}]*\}/i;
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/project/ProjectFaviconResolver.ts around line 62:

`LINK_ICON_OBJ_RE` now matches the first `{...}` object containing `rel: "icon"` even when that object has no `href`, so `extractIconHref` never examines later valid declarations. A source like `[{ rel: "icon" }, { rel: "icon", href: "/favicon.svg" }]` returns `null` instead of `/favicon.svg`. The old regex required both `rel` and `href` in the same candidate. Consider restoring the `href` lookahead inside `LINK_ICON_OBJ_RE` so non-`href` objects are skipped.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4b77158. Configure here.

const objMatch = source.match(LINK_ICON_OBJ_RE);
if (objMatch?.[1]) return objMatch[1];
const objHref = objMatch?.[0].match(ICON_HREF_RE);
if (objHref?.[1]) return objHref[1];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skips later valid icon objects

Medium Severity

LINK_ICON_OBJ_RE now matches any brace group with rel: "icon" and no longer requires a quoted href in that same object. extractIconHref then only inspects the first match, so if that object has no extractable href (missing, backtick template, or variable), a later valid icon object is never considered and resolution returns null.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4b77158. Configure here.

@macroscopeapp

macroscopeapp Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

2 blocking correctness issues found. Multiple unresolved review comments identify functional regressions where the new regex pattern fails to match valid icon metadata (nested braces, objects without href). These bugs could cause favicon resolution to fail in legitimate cases and should be addressed before merging.

You can customize Macroscope's approvability policy. Learn more.

@vbb-it vbb-it closed this Aug 6, 2026
@vbb-it

vbb-it commented Aug 6, 2026

Copy link
Copy Markdown
Author

Closing this — it was opened from the wrong account. Superseded by #5538 (issue moved to #5537).

The bot findings here were both correct and are fixed in the new PR: anchoring the pattern on { broke metadata sitting beside a nested object, and stopped at the first rel: "icon" even when that object had no href. #5538 scans brace-free runs instead, which reproduces the current behavior on both cases while staying linear. Sorry for the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Unanchored favicon regex pins the server event loop, wedging the environment into a permanent reconnect loop

2 participants