Troubleshooting
Fix each Verify installation verdict, a widget that does not show, a blocking Content Security Policy, attributes that go nowhere, and a crawl that fails.
arcy.js never throws into your Product. Every problem it hits becomes a console.warn
prefixed [arcy], so open the browser console first: the line there names the problem. Every
warning's exact text, cause and fix is in the Console warnings
table; the sections below cover the problems that take more than one line to fix.
The sections are numbered so you can point at one, and the numbers match on the Turkish page.
1. The widget does not show up
Look for this first:
[arcy] Failed to load. ARCY is disabled.The script itself never loaded. An ad blocker, your Content Security Policy (section 2), or a page that navigated away before the script finished are the usual causes.
Without that line, arcy.js loaded but did not mount. Two messages say why:
[arcy] The page has no <body> to mount into yet. The widget will not render.init() ran before the body existed, usually a script in the head without defer or async.
Move the script tag to the end of the body, or call arcy.init() after the DOM is ready.
[arcy] This ARCY surface is already on this page. Ignoring the second mount.
[arcy] Another copy of ARCY is already on this page. Ignoring this one.The snippet is installed twice: the npm path and the HTML path on one page, or the HTML snippet pasted into both a layout and a tag manager. Keep one install path and remove the other.
2. Your Content Security Policy is blocking the widget
The widget reports the whole fix once, the first time your policy blocks something of ours:
[arcy] This page's Content-Security-Policy is blocking part of the ARCY widget.
Add these to the policy your app sends:
connect-src https://api.arcyai.com
font-src https://api.arcyai.com
img-src https://api.arcyai.com blob:
style-src 'unsafe-inline', or a nonce on the script that loads ARCYOn the HTML snippet path the list also carries script-src https://cdn.arcyai.com, because
the snippet is a script tag pointing there. On the npm path the chat panel, the flow engine and
the picker are part of your own bundle and need nothing.
A directive you did not write is not enforced, and nothing falls back except through
default-src. So a policy with no default-src needs nothing added, and a policy with one
needs the lines above. Your full policy is the Content-Security-Policy response header on the
document request in the browser's network tab.
The blob: in img-src is what lets a visitor see the image they attached before they send it.
Where to put it
If you already build the policy in middleware.ts, add the directives there. Otherwise:
// next.config.js
const csp = [
"connect-src 'self' https://api.arcyai.com",
"font-src 'self' https://api.arcyai.com",
"img-src 'self' blob: https://api.arcyai.com",
].join("; ")
module.exports = {
async headers() {
return [
{
source: "/:path*",
headers: [{ key: "Content-Security-Policy", value: csp }],
},
]
},
}// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
"/**": {
headers: {
"Content-Security-Policy": [
"connect-src 'self' https://api.arcyai.com",
"font-src 'self' https://api.arcyai.com",
"img-src 'self' blob: https://api.arcyai.com",
].join("; "),
},
},
},
})SvelteKit generates the policy itself, so add the directives to the config:
// svelte.config.js
export default {
kit: {
csp: {
directives: {
"connect-src": ["self", "https://api.arcyai.com"],
"font-src": ["self", "https://api.arcyai.com"],
"img-src": ["self", "blob:", "https://api.arcyai.com"],
},
},
},
}// src/middleware.ts
export const onRequest = async (context, next) => {
const response = await next()
response.headers.set(
"Content-Security-Policy",
[
"connect-src 'self' https://api.arcyai.com",
"font-src 'self' https://api.arcyai.com",
"img-src 'self' blob: https://api.arcyai.com",
].join("; "),
)
return response
}// app/entry.server.tsx
responseHeaders.set(
"Content-Security-Policy",
[
"connect-src 'self' https://api.arcyai.com",
"font-src 'self' https://api.arcyai.com",
"img-src 'self' blob: https://api.arcyai.com",
].join("; "),
)app.use(
helmet.contentSecurityPolicy({
directives: {
connectSrc: ["'self'", "https://api.arcyai.com"],
fontSrc: ["'self'", "https://api.arcyai.com"],
imgSrc: ["'self'", "blob:", "https://api.arcyai.com"],
},
}),
)If you cannot set a header, set the policy in a <meta> tag in the head, before every script on
the page:
<meta
http-equiv="Content-Security-Policy"
content="connect-src 'self' https://api.arcyai.com; font-src 'self' https://api.arcyai.com; img-src 'self' blob: https://api.arcyai.com"
/>The panel renders unstyled
The widget adds its own style elements. With style-src 'unsafe-inline' there is nothing to
do. With a nonce there is nothing to do either: the widget reads the nonce off the script that
loaded it and stamps it on everything it injects. If the panel is unstyled and the console says
nothing, this is where to look.
To allow no outside origin
Rewrite a path on your own domain to our API and pass it as apiBase. Requests, uploads and
fonts then all come from your own origin.
// next.config.js
async rewrites() {
return [{ source: "/_arcy/:path*", destination: "https://api.arcyai.com/:path*" }]
}arcy.init("YOUR_ENVIRONMENT_TOKEN", { apiBase: "/_arcy" })That closes everything on the npm path. The HTML snippet path still needs
https://cdn.arcyai.com in script-src.
3. Verify installation says no traffic yet
arcy.init() never reached the selected environment. In order of likelihood:
- The page that calls
arcy.init()has not been opened in a browser since you deployed it. Open it and reload while the check is watching. - The token in
init()belongs to another environment. Copy it again from Installation with the right environment chosen in the top row. - A network error is swallowing the call. Look for the request
init()makes in the browser's network tab.
4. Verify installation says the snippet carries another environment's token
Traffic is arriving, but from a snippet copied off a different environment's Installation page, for example the Development snippet in a Production deploy. Either switch the top row to the environment that is receiving, or replace the token in the live snippet with the one Installation shows.
5. Verify installation says a Site is not under a verified Domain
The token is right, but the request came from a Site under none of your workspace's verified
Domains. The verdict names the refused Site. If it is yours, add and verify its Domain, or a
name above it, on Domains. localhost never needs this.
6. Verify installation says identity verification is failing
Sessions arrive, but the userHash you pass does not verify against the environment's Secret.
While Require identity verification is on, every affected session is served as anonymous, so
attributes and personalization stop for those users.
Check your signing code against the
recipe: HMAC-SHA256 of the exact string
you pass to identify(), keyed with the Secret, hex encoded. The usual cause is a type
mismatch: ids that are integers on the server and strings in the browser. A signature over
42 does not verify a call identifying "42". If you replaced the Secret, redeploy the new
one; what replacing does is on API Keys.
7. A custom attribute is not showing up
Verify installation lists unknown attributes by name: a key that arrives without a declaration is dropped, never stored. Declare it on Attributes and send it again. Nothing is backfilled for traffic that arrived undeclared.
If the console shows this instead, the value's shape is the problem:
[arcy] identify() dropped "some_key": an attribute must be a string, number,
boolean, null, or an array of strings.Flatten a nested object, a function or a Date to one of those shapes before passing it.
8. identify() or updateUser() seems to do nothing
[arcy] identify() needs a user id. Ignoring the call.
[arcy] identify() was called before init(). Ignoring.The first means the user id was empty, undefined or not a string. The second is a race
between your auth state and the script tag: call identify() where your session becomes
available, after init() has run once on the page.
updateUser() before any identify() or identifyAnonymous() sends nothing, because there is
no profile to update yet.
9. @types/arcy.js returns a 404
npm error 404 Not Found - GET https://registry.npmjs.org/@types%2farcy.jsThat package does not exist. arcy.js ships its own TypeScript types, so remove the line and
install only arcy.js. If your editor still reports missing types, run
npm install arcy.js@latest.
10. The chat panel stopped opening on a tab left open for a long time
ARCY ships the chat panel, the flow engine and the picker as separate files with a version stamp in the name. A page open since before a release still holds the old names, so the first click asks for a file that is gone. arcy.js then asks the CDN for the current names and loads those; the visitor sees the panel open and nothing is logged.
Act only if the panel still does not open and you see:
[arcy] Failed to load https://cdn.arcyai.com/arcy.chat.<version>.js. The chat panel will not
open. If this page sets a Content-Security-Policy, it must allow scripts from
https://cdn.arcyai.com.Then the retry failed too, and the cause is your policy (section 2), an ad blocker or corporate proxy on the visitor's machine, or a filtering gateway on your network. A reload always works. Do not build a reload prompt into your Product for this.
11. A web crawler source reads Failed
The toast says training failed and the row reads Failed. Point at Failed, or tab to it, for the reason; the toast shows the same sentence. If the reason names an address that could not be found, a refusal, a server error or a timeout, fix the site or the Starting URL and press Train again. If it says the pages could not be prepared, press Train again, and contact us if it happens again.
The most common reasons come from the site's own robots.txt. View log on the row shows
Skipped: against every page it kept ARCY from. Read the file from your terminal:
curl https://yoursite.com/robots.txtThe reason says the site "blocks bots in its robots.txt file". The rules written for every bot are in force, either because the source has Follow robots.txt on or because its Site is under no verified Domain and is not your work email's domain (ARCYBot). The usual file blocks every crawler from every page:
User-agent: *
Disallow: /This is what a staging or preview build ships. On Next.js the common cause is an
"indexable" environment variable left unset in production, so the app/robots.ts file falls
through to its block-everything branch. It also keeps search engines off your site.
If the Site is under a verified Domain, turn Follow robots.txt off on the source and press Train: nothing to deploy. Otherwise fix the file: make it allow crawling in production, or add a group for ARCY that leaves search engines blocked:
User-agent: ARCYBot
Allow: /The reason says the site's robots.txt "has a rule for ARCYBot that blocks it". A group
that names ARCYBot is always obeyed, whatever Follow robots.txt says. Change that group
to Allow: / or delete it.
After either file change, deploy, run the curl again until the block is gone, then press
Train.
www.yoursite.com and yoursite.com do not need separate treatment: one redirects to the
other, and the crawler follows the redirect to the same robots.txt.