Skip to content

Add explicit errors for unknown RRI prefixes#5359

Draft
backspace wants to merge 4 commits into
mainfrom
rri-relationship-link-cs-11731
Draft

Add explicit errors for unknown RRI prefixes#5359
backspace wants to merge 4 commits into
mainfrom
rri-relationship-link-cs-11731

Conversation

@backspace

Copy link
Copy Markdown
Contributor

When I ran the migration script in staging, there were problems with boxel-homepage. The theme relationship reference @cardstack/base/Theme/boxel-brand-guide was turning into this because the base RI wasn’t registered: https://realms-staging.stack.cards/boxel-homepage/boxel-ai-website/Site/@cardstack/base/Theme/boxel-brand-guide

While I’m still looking into why the base RI isn’t found, this PR adds an explicit error in VirtualNetwork#resolveRRI when a prefix isn’t registered, instead of constructing invalid URLs.

A relationship link in scoped prefix form (e.g. `@cardstack/base/Theme/x`)
that the resolving VirtualNetwork has no mapping for was silently joined
onto the referring card's URL, producing a bogus
`https://realm/card/@cardstack/base/Theme/x` that 404s — surfacing only as
a confusing downstream render error and a malformed dependency entry.

- VirtualNetwork.resolveRRI: throw a clear "no mapping registered for that
  prefix" error for an `@`-scoped reference that matches no registered
  realm mapping, instead of falling through to `new URL(ref, base)`.
  Serialization/relativization preserves scoped refs verbatim and never
  reaches this path; canonicalURL catches the throw and keeps the
  reference in clean prefix form, so dependency extraction emits a clean
  RRI rather than a mangled URL.
- card-api resolveRef: when no VirtualNetwork is available, return an
  `@`-scoped reference unchanged rather than path-joining it into a bogus
  URL (`new URL` does not throw on these when a base is present, contrary
  to the previous comment's assumption).
- Add regression coverage for scoped relationship-link resolution:
  resolves to the mapped realm when registered, throws when not.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Host Test Results

    1 files  ±0      1 suites  ±0   2h 15m 38s ⏱️ +44s
3 320 tests +1  3 304 ✅ +1  15 💤 ±0  0 ❌ ±0  1 🔥 ±0 
3 339 runs  +1  3 322 ✅ +1  15 💤 ±0  1 ❌ ±0  1 🔥 ±0 

Results for commit 13691d8. ± Comparison against earlier commit bb4d582.

For more details on these errors, see this check.

Realm Server Test Results

    1 files  ±0      1 suites  ±0   10m 23s ⏱️ +10s
1 673 tests ±0  1 673 ✅ ±0  0 💤 ±0  0 ❌ ±0 
1 752 runs  ±0  1 752 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 13691d8. ± Comparison against earlier commit bb4d582.

backspace and others added 3 commits June 29, 2026 13:34
internalKeyFor/internalKeysFor canonicalize a CodeRef into a storage key
by routing the module through resolveURL. For a scoped RRI whose realm
prefix isn't registered in the caller's VirtualNetwork (e.g. an index
writer keying a base ref against a bare VN), resolveURL now throws on the
unknown prefix instead of returning a path-joined URL — breaking key
generation.

A scoped RRI is already the canonical module identifier, so when its
prefix isn't registered, use it as-is rather than resolving it. Registered
prefixes and URL/relative refs keep resolving through resolveURL exactly
as before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A relationship link that is still loading or broken can surface an
undefined model to a card's own fitted/embedded template — the linksTo
component only swaps in the broken-link template for specific membership
states, otherwise it renders the field's declared card class against an
undefined value. A custom template that calls `{{cardTypeDisplayName
@model}}` then dereferences `undefined.constructor` and throws, failing
the entire card render (and, during indexing, erroring the card).

Guard the undefined case like the sibling helpers cardTypeIcon and
getFieldIcon already do, returning an empty string so the template
renders empty instead of crashing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
copyFrom rewrites a source realm's URLs to the destination realm, guarding
registered prefixes so they pass through unchanged. But a scoped RRI whose
prefix isn't registered in the writer's VirtualNetwork (e.g. a base ref
keyed by an index writer with a bare VN) fell through to `new URL(value)`,
which throws on a bare `@scope/realm/...` string.

A scoped RRI is an absolute cross-realm identifier and must never be
rewritten to the destination realm regardless of registration. Guard the
three copy sites (copyURL, objectWithCopiedRealmKeys, updateIds) on the
leading `@` so they preserve such refs verbatim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant