If this page does not cover your case, write to support@grainql.com.
First checks
- Open the developer tools of the browser (F12, or Cmd+Option+I) and read the Console tab.
- Type
window.GrainTagin the console. If it returnsundefined, the Grain Tag did not load. - Open the Network tab and look for failed requests to
clientapis.grainql.com. - Set
debug: truein the SDK configuration. Then the SDK logs each event and each send.
GrainTag.init({
tenantId: 'your-tenant-id',
debug: true,
});
debug: true before you deploy.
Authentication (401)
| Symptom | Cause | What to do |
|---|---|---|
| The dashboard shows “Your session has expired” or “Authentication required” | The login session expired after inactivity, or the browser cleared or blocked the cookies | Click Reload Page in the message. If the message returns, sign out and sign in again. |
| The dashboard signs you out again and again | Third-party cookies are blocked, an ad blocker or tracking protection acts on grainql.com, or the browser is in private mode | Allow cookies and disable blockers for grainql.com. Clear the cache and cookies for grainql.com. Try another browser to isolate the cause. |
The SDK receives 401 | The tenantId is wrong, or the secret key does not match the tenant | Compare the tenant alias with the dashboard settings. Read Authentication. |
The Query API returns 401 | The X-API-Key header is missing or holds a wrong key | Send a valid API key. Read Query API. |
Network
| Symptom | Cause | What to do |
|---|---|---|
| ”Failed to fetch”, “Network error: Unable to connect to server”, or “Network connection issue” | No internet connection, or a firewall, proxy, VPN, or DNS failure blocks the request | Make sure that https://clientapis.grainql.com/health opens in the browser. If it does not, the network blocks analytics traffic. |
| Requests fail on a corporate network only | The firewall or proxy blocks clientapis.grainql.com | Allow clientapis.grainql.com, or all of *.grainql.com, in the firewall. If the proxy needs authentication, ask the IT department to allow analytics traffic. |
| Requests fail for some visitors only | An ad blocker or a privacy extension blocks Grain | Disable the extensions, or add grainql.com to the allow list of the extension. Load the site in a private window without extensions to isolate the cause. |
Server (5xx)
| Symptom | Cause | What to do |
|---|---|---|
| ”API error 500”, “Service temporarily unavailable”, or “Our servers are experiencing issues” | A failure on the Grain side. These failures are usually temporary. | Wait 1 to 2 minutes, then reload the page. Read https://status.grainql.com for known incidents. |
The 5xx responses continue for more than 5 minutes | An ongoing incident | Write to support with the error message and code, the time, and the action that failed. |
Rate limit (429)
| Symptom | Cause | What to do |
|---|---|---|
| ”Rate limit exceeded” or “Too many requests” from the event endpoint | The site sends more events than the plan allows | Track meaningful interactions only. Do not track mousemove or every scroll event. Throttle high-frequency handlers. |
429 from the Query API | The API key used more requests than the plan allows per minute or per day | Read the Retry-After header and wait that many seconds. Cache query results on the client. Combine several queries into one. Use a longer interval for automatic refresh. |
Validation (400)
| Symptom | Cause | What to do |
|---|---|---|
400 on init | The tenantId is too short or holds spaces | Use the tenant alias from the dashboard: 4 to 32 characters, letters, digits, and hyphens. |
400 on track | The event name is empty or holds special characters | Use names such as my_event or button-clicked. Read Name events. |
400 on track with properties | A property holds a function or a circular reference, for example window | Send strings, numbers, booleans, arrays, and plain objects only. Read Automatic properties. |
Script not loading
| Symptom | Cause | What to do |
|---|---|---|
window.GrainTag is undefined, the console shows GrainTag is not defined, and the Network tab shows no request to clientapis.grainql.com | The script tag comes after the code that uses it, or the URL has a typo | Put <script src="https://tag.grainql.com/v4/your-tenant-id.js"></script> in <head>, before any code that calls it. Make sure that the Network tab shows the script with status 200. |
| The npm import runs but nothing is tracked | The package is not installed, or init did not run | Run npm list @grainql/tag. Import init and call it with the tenant alias. Calls to track and identify before init are queued and replayed after init. |
| The tag does not fire from Google Tag Manager | The tag did not fire, the trigger is wrong, or the container is not published | In GTM preview mode, look under Tags Fired. Set the trigger to Initialization - All Pages. Publish the container. Clear the browser cache. Read Google Tag Manager. |
Events missing in the dashboard
| Symptom | Cause | What to do |
|---|---|---|
| Events were sent and do not show | The dashboard shows events after a delay of up to 30 seconds | Wait 30 seconds, then reload the dashboard. |
| Events never show | The events go to another tenant | Compare the tenantId in the configuration with Settings in the dashboard. |
| Some events do not show | A filter hides them: a narrow date range, an event name filter, or a user filter | Reset the filters. Select Last 30 days. |
No events show in opt-in or GDPR_STRICT mode | The visitor did not grant consent | Call consent.grant(['analytics', 'functional']) on the Grain Tag, or grantConsent on Analytics Web. Read Privacy and consent. |
The Network tab shows failed POST requests to /v1/events | A CORS error, or a 4xx or 5xx status | Read the CORS section and the status code table on this page. |
| The dashboard shows “Error fetching dashboard data” or “Failed to load analytics” | No data in the date range, no events received, missing permissions, or a stale cache | Select Last 30 days. Open Database view, then the Events tab, and make sure that events exist. Make sure that your account has viewer or editor access to the tenant. Clear the cache or use a private window. |
| Mission Control does not load | No goal exists, or a goal has fewer than 10 events | Open Mission Control, click Configure Goals, and create a goal. Then wait for 10 events or send test events. |
CORS
| Symptom | Cause | What to do |
|---|---|---|
| ”Access to fetch at ‘https://clientapis.grainql.com/…’ blocked by CORS policy” or “No ‘Access-Control-Allow-Origin’ header” | The origin of the page is not in the allowed origins of the tenant | Open Settings, then Allowed Origins, in the dashboard. Add the origin. |
https://example.com
https://*.example.com
http://localhost:3000
http://localhost:5173
http://192.168.1.100:3000
https://*.example.com allows every subdomain.
TypeScript
| Symptom | Cause | What to do |
|---|---|---|
Cannot find module '@grainql/analytics-web' | The package is not installed, or the module resolution is wrong | Run npm install @grainql/analytics-web. The package ships its own types. Set "moduleResolution": "node" and "esModuleInterop": true in tsconfig.json. |
Property 'GrainTag' does not exist on type 'Window' | The types of @grainql/tag are not in the compilation | Install @grainql/tag. Its types declare the GrainTag global for the script build. Analytics Web declares no global. |
| A property object does not type-check | track takes Record<string, unknown> | Declare your own interface for the properties and pass the object. Read TypeScript. |
Performance
| Symptom | Cause | What to do |
|---|---|---|
| The page load is slow after the install | The script tag blocks the parser | Add the async attribute to the script tag. |
| The bundle grew after the npm install | The SDK is imported synchronously into the main bundle | Import it with await import('@grainql/analytics-web') in the code that needs it. |
| The browser stutters on scroll | A handler calls track on every scroll event | Throttle the handler to one call per second at most. |
Platform-specific
| Platform | Symptom | Cause | What to do |
|---|---|---|---|
| Next.js | Cannot use import statement outside a module | The SDK is imported on the server side | Import @grainql/tag. The import has no side effects and returns a no-op stub outside a browser. Or load the script tag with next/script. Read Next.js. |
| React | An event fires two times in development | React 18 StrictMode runs effects two times in development only | No action. Production fires the event one time. Read React. |
| Shopify | Events stop after a theme change | The theme interferes with the script | Read Shopify. |
| WordPress | Events stop after a plugin install | A caching or optimization plugin changes or delays the script | Read WordPress. |
Status codes
| Status | Where | Cause | What to do |
|---|---|---|---|
400 | The event endpoint | A wrong tenantId, event name, or property value | Read the Validation section on this page. |
401 | The dashboard, the event endpoint, the Query API | An expired session, a wrong tenant alias or secret key, or a wrong API key | Read the Authentication section on this page. |
429 | The event endpoint, the Query API | The plan limit is used up | Wait for the Retry-After seconds, then send fewer requests. |
500, 502, 503 | Any Grain endpoint | A failure on the Grain side | Wait 1 to 2 minutes and retry. |