Troubleshooting
Known QR code failure modes, their cause, and how to confirm it.
A saved QR code 500s on download or render
Symptom: A code that used to render fine now 500s from
GET /api/admin/qr/{domain}/{qr_id}/image.svg.
Cause: The design references an icon file (a logo: "icon:<name>", or the tease brand
mark) that was on disk when the design was saved but has since been removed. Validation checks
the file exists at save time — it doesn't re-check at render time — so the stored config still
points at a name or path that no longer resolves.
Check: Look at the code's stored design_json for a "logo": "icon:<name>" value, then
confirm <name>.svg still exists under the icons static directory
(backend/app/products/link/core/qr/design.py:292-326 reads it, list_icon_names() at line 74
lists what currently exists). If the icon is gone, either restore the file or PATCH the code to
a different logo/preset — see Design a QR code.
Domain rotation or rollback is blocked with no_qr_codes
Symptom: Rotating a domain to a new hostname, or rolling back a migration, fails with a block naming QR codes.
Cause: At least one QrCode row exists on the address being moved. This is by design, not a
bug: a QR code is printed onto a physical carrier out in the world, and there's no way to
auto-migrate ink on paper — silently moving or invalidating the domain underneath a printed code
would 404 or mislabel every scan of it from that point on.
Check: domain_qr_count(session, old, new) > 0
(backend/app/products/link/core/domain_carrier_guard.py:105-135) is what the rotation guard
calls. Before rotating, either wait until the printed carriers are fully retired, or use
Rename or disable a QR code to move each
code's scan path onto the new address deliberately — there is no bulk auto-migration.
What's next
Back to QR codes for the full task list.