Troubleshooting: Common Issues and Solutions
Solve common problems with the Qaid feedback embed including loading issues, CORS errors, and styling conflicts.
Almost everything that goes wrong here shows up in the browser console within a second of loading the page. Open it first, then work down this list.
Nothing appears on the page
The script never loaded
Check the tag is there and sits before the closing </body>:
<script
src="https://unpkg.com/@qaiddev/thumbs-embed/dist/embed.js"
data-api-key="your-api-key"
></script>
A 404 or a network failure in the console tells you the rest.
No endpoint
data-endpoint is what the embed posts to. Without it there is nowhere to send anything:
<!-- Nothing to submit to -->
<script src="https://unpkg.com/@qaiddev/thumbs-embed/dist/embed.js"></script>
<!-- Endpoint set; the key attributes the feedback to a project -->
<script
src="https://unpkg.com/@qaiddev/thumbs-embed/dist/embed.js"
data-endpoint="/api/feedback"
data-api-key="your-project-api-key"
></script>
data-api-key is optional as far as the embed is concerned. Leave it out and submissions arrive unattached to a project, which is almost never what you wanted.
The container isn't there yet
A container selector that matches nothing renders nothing:
<!-- The container must exist -->
<div id="feedback-embed"></div>
<script
src="https://unpkg.com/@qaiddev/thumbs-embed/dist/embed.js"
data-api-key="your-api-key"
data-container="#feedback-embed"
></script>
The script ran before the DOM
Constructing by hand means waiting for the document:
document.addEventListener('DOMContentLoaded', () => {
new QaidFeedback({
apiKey: 'your-api-key',
// ... other options
});
});
CORS errors
The browser blocked the request before it left. You will see something like:
Access to fetch at 'https://api.qaid.dev/feedback' from origin 'https://yoursite.com'
has been blocked by CORS policy
Qaid's own /api/feedback answers every origin, so a CORS error against it usually means the request never reached it. Check the URL in data-endpoint first.
A page served over HTTPS posting to an HTTP endpoint is blocked as mixed content, which reads like a CORS error and is not one.
Pointing at your own endpoint instead? Then the headers are yours to send:
Access-Control-Allow-Origin: https://yoursite.com
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-API-Key
The embed looks unstyled or broken
The embed renders in a shadow root, so your page CSS mostly cannot reach it. What can reach it is anything inherited, and a reset broad enough to hit everything:
/* This can break the embed */
* {
all: unset;
}
/* Or aggressive box-sizing */
* {
box-sizing: content-box;
}
In DevTools, expand the #shadow-root on the embed's host element to see what is actually applied inside it. To change any of it, pass your own rules through the css option rather than writing them in your page stylesheet, where they will not apply.
The colours are the defaults
Colours are settings rather than CSS:
<script
src="https://unpkg.com/@qaiddev/thumbs-embed/dist/embed.js"
data-api-key="your-api-key"
data-primary-color="#6366f1"
></script>
Buttons hidden or unclickable
A button you can see but cannot click is sitting under something transparent. The embed defaults to z-index: 50. Find what beat it:
// Find elements with high z-index
document.querySelectorAll('*').forEach(el => {
const z = getComputedStyle(el).zIndex;
if (z !== 'auto' && parseInt(z) > 50) {
console.log(el, z);
}
});
Lower the other thing rather than raising the embed, or you will do this again next month with something else.
If a high zIndex changes nothing, the embed is trapped in a parent's stacking context. Look up the tree for a positioned ancestor with its own z-index, or one with a transform, a filter, or an opacity below 1. Any of those starts a new context and caps everything inside it.
Clicking does nothing
Read the console. "QaidFeedback is not defined" means the script did not load, so go back to the first section. A TypeError or ReferenceError usually means another script on the page threw first and stopped this one running.
Something else is eating the click
A global handler calling stopPropagation will do it:
// Check if something is calling stopPropagation
document.addEventListener('click', (e) => {
console.log('Click reached document:', e.target);
}, true);
Works on desktop, not on a phone
Usually touch-action or a pointer-events rule from a scroll library:
/* This can prevent interactions */
.some-container {
touch-action: none;
}
The submit succeeds but nothing lands
Open the Network tab and find the POST. Its status tells you which of four things happened.
A 400 means a required field was missing from the body, and a 401 means the API key matches nothing or has been revoked. With 403, your project has a domain restriction that this page URL does not satisfy; localhost and 127.0.0.1 are always allowed regardless. A 410 means the project is archived, so restore it or point the key at a live one.
The key itself
Project API keys have no prefix and no test/live split. They are opaque strings you copy from Settings → API Keys, and there is exactly one kind. If a key stopped working, check it has not been revoked there.
The request never left
A blocked request shows as failed with no status at all. Ad blockers and privacy extensions account for most of these; a service worker intercepting fetches accounts for the rest.
Recording doesn't start
The browser can't
Feature-detect before you blame anything else:
// Check if screen capture is supported
if (!navigator.mediaDevices?.getDisplayMedia) {
console.log('Screen capture not supported in this browser');
}
Chrome 72, Firefox 66, Edge 79 and Safari 13 upward. Safari's support is partial. On phones this path is not used at all, because touch devices fall back to DOM screenshots.
Permission denied
The reader closed the share dialog without picking anything, or denied it once before and the browser remembers. Reset it in site settings. And screen capture needs a secure context: HTTPS, or localhost.
Policy
Managed browsers often disable screen capture outright, which surfaces as "Not allowed" and cannot be worked around from your side.
It records but never uploads
The file is too big, or the connection dropped part way. Lower videoOptions.maxDuration and try again.
Reading the console messages
net::ERR_BLOCKED_BY_CLIENT
An extension blocked the script. You cannot ask your visitors to allowlist you, so self-host it instead.
Cannot read property 'x' of null
An element that was expected is not there. Nearly always a container selector that matches nothing, or a script running before the DOM is built.
SecurityError: Blocked a frame with origin...
The embed cannot see inside a cross-origin iframe, so it cannot target or screenshot what is in one.
QuotaExceededError
localStorage is full. The embed stores almost nothing, so something else on the page filled it. Find out what:
// Check localStorage usage
let total = 0;
for (let key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
total += localStorage[key].length * 2; // UTF-16 = 2 bytes per char
}
}
console.log('localStorage used:', (total / 1024).toFixed(2), 'KB');
Still stuck
Search the open issues at github.com/qaiddev/thumbs-embed, or mail team@qaid.dev.
Either way, send six things: your browser and version, your OS, whatever the console printed, whatever the Network tab shows for the failed request, your script tag or config exactly as it is on the page, and the steps that reproduce it. A report with all six usually gets answered on the first reply instead of the third.
// Gather diagnostic info
console.log({
userAgent: navigator.userAgent,
url: window.location.href,
timestamp: new Date().toISOString(),
// Add any error messages you're seeing
});
Most issues can be resolved by checking the browser console, verifying your configuration, and ensuring proper script loading. When in doubt, enable debug mode to see exactly what the embed is doing.