The widget doesn’t render at all
Symptom. You imported and mounted<ProbotBot /> but nothing appears
on the page - not even the floating bubble.
Cause 1: not inside a client component (Next.js App Router)
<ProbotBot /> uses React hooks and event handlers, so the containing
file must start with "use client";.
Fix:
app/page.tsx
without "use client";), Next.js silently drops the render.
Cause 2: React 18+ isn’t in the dep tree
The package’s peer dependencies requirereact >= 18 and
react-dom >= 18. Older React ships without concurrent-mode APIs the
package uses.
Fix:
npm i react@^18 react-dom@^18.
Cause 3: CSS is overriding the widget
The IIFE build injects its styles into<head> at mount time. If your
site has a very aggressive !important reset (Tailwind’s preflight
plus a custom reset, for instance), it can obliterate the FAB.
Fix. Open DevTools → Elements → find the .probot-fab element → check
the computed styles. If your reset stripped position/width/height/z-index,
add a scoping selector to your reset so .probot-root * is excluded, or
raise the widget’s z-index in your global CSS after import.
Cause 4: You mounted the vanilla IIFE at a selector that doesn’t exist
ProbotSelfHosted.mount("#probot", …) throws if #probot is missing.
Fix. Check the browser console for
probot-self-hosted: target not found: #probot and either add the target
element to your HTML or move the <script> block to the bottom of <body>
so it runs after the DOM is ready.
Every reply says “chat_failed”
Symptom. The user types, the “thinking” bubble appears, then the assistant response is the literal stringchat_failed or llm_500.
Cause 1: your sendMessage threw
The widget renders whatever sendMessage throws as the assistant
message. Open Network → your /api/probot-chat route → look at the
response.
Common patterns:
500 chat_failedfrom your route → the LLM provider returned an error. Check server logs.401from the provider → the API key env var is unset or wrong.404 not_foundfrom the SPA host → your dev proxy isn’t forwarding/api/*to the backend (Vite: checkvite.config.tsproxy; Next.js dev: check the route file path).
{ "reply": "OK" }, retry the widget.
Cause 2: your handler returns the wrong JSON shape
The widget readsdata.reply from the response. If your handler returns
{ "response": … } or { "message": … } or the LLM provider’s raw
choices[0].message.content shape, the widget sees undefined and
throws.
Fix. Return exactly { "reply": "…" }:
Cause 3: your handler streams instead of returning JSON
If you set up SSE / chunked streaming but the widget is expecting non-streaming JSON, it will parse the first chunk as JSON, fail, and throw. Fix. Either switch the widget to a customsendMessage that reads
the stream (see Next.js § streaming)
or make the handler non-streaming.
Conversations don’t appear in the dashboard
Symptom. Your local chat works, but the dashboard’s Conversations tab stays empty. Same for Leads.Cause 1: you didn’t pass dashboard.token
Without a dashboard prop, the widget makes zero calls to
pro-bot.dev. That’s by design - dashboard analytics are opt-in.
Fix.
console.log(process.env.NEXT_PUBLIC_PROBOT_TOKEN)
inside the client component should print pbt_…, not undefined.
Cause 2: the token is wrong or revoked
The widget swallows analytics failures silently (they must never break chat). So a 401 from/api/v1/bot/conversations gives no visible
symptom locally.
Fix. Test the token directly:
400→ token is valid; body validation failed as expected. ✓401→ token is unknown, revoked, or malformed. Regenerate.
Cause 3: prod build lost the env var
NEXT_PUBLIC_* env vars must be defined at build time, not just runtime.
If you set NEXT_PUBLIC_PROBOT_TOKEN on Vercel after the last build,
existing bundles still have undefined inlined.
Fix. Trigger a fresh build / redeploy after setting the env var.
Cause 4: CORS / firewall between your app and pro-bot.dev
Vercel and other serverless hosts occasionally drop egress to third-party domains under strict outbound rules. Rarer but real. Fix. From your web app’s runtime environment (a Vercel Function, an Express server, etc.) confirm you can reachpro-bot.dev:
Cause 5: dashboard is caching a stale view
The dashboard’s analytics tabs poll rather than subscribe. New rows show up within a couple of seconds; if you don’t see one after 30 seconds, it’s not a caching issue.chat_failed on some replies but not others
Symptom: about half of replies work; the others crash with chat_failed.
Cause: the LLM’s response exceeded max_tokens
createOpenAIHandler sets max_tokens: 1024 by default. Longer replies
get truncated by the provider and can return odd payloads that break
JSON parsing.
Fix. Raise the ceiling or use gpt-4o (higher default max) instead
of gpt-4o-mini:
customInstructions of
"Keep replies under 5 sentences." so the model doesn’t run away.
The LLM key appears in the browser bundle
Symptom. Security scanner flags the key. DevTools → Sources showsOPENAI_API_KEY = "sk-…".
Cause: you used NEXT_PUBLIC_ (or Vite’s VITE_) prefix on the key
Anything with those prefixes is inlined into the client bundle by
design.
Fix.
- Remove the offending env var immediately.
- Rotate the key at your LLM provider - assume it’s compromised.
-
Set the un-prefixed version on the server only:
-
Call it from
app/api/probot-chat/route.ts(Next.js) or your Express handler - not from the React component.
The chat cuts off mid-reply on Vercel
Symptom. Long LLM responses truncate after ~10 seconds. Vercel’s Function logs showTask timed out after 10.00 seconds.
Cause: Vercel’s Edge runtime has a short timeout budget
The Edge runtime tops out at 25 seconds for Pro accounts, less for Hobby. The Node runtime is much more generous. Fix. Force Node in your route:The widget disappears after route change on my SPA
Symptom. First render shows the widget; after a client-side navigation it vanishes.Cause: the widget’s parent unmounted and re-mounted, losing state
Mount<ProbotBot /> once at the layout level, not inside per-route
components. That way a route change doesn’t unmount it.
Fix (Next.js App Router):
<BotWidget /> in your App (the
outer shell), not inside a Route’s element.
Suggested-question chips render but clicking does nothing
Cause: your sendMessage throws synchronously
useProbotChat treats send() as async and catches thrown errors, but
if sendMessage synchronously throws before returning a promise, the
click handler swallows it and nothing appears.
Fix. Make sure sendMessage is async:
I want to migrate from the old cloned probot-bot runtime
Earlier ProBot releases shipped a separate probot-bot Next.js runtime
that you cloned into its own repo and deployed. That model is
discontinued.
Migration steps:
npm i probot-self-hostedin your existing web app (not the runtime repo).- Move the LLM key env var from the old runtime’s
.envto your web app’s server-side env (Vercel dashboard,.env.local, whatever). - Add the server-side chat proxy route to your web app.
- Add
<ProbotBot />to your UI. - Verify the widget works locally (curl the proxy, then click through the widget).
- Point your domain from the old runtime to the new web app deployment.
- Your existing
pbt_…tokens keep working - the platform still trusts them for the/api/v1/bot/{conversations,leads}endpoints, no re-mint needed. - Once the new deployment is verified, decommission the old
probot-botruntime + its DNS.
/api/v1/bot/config and /api/v1/bot/knowledge
endpoints have been removed - if any tooling still POSTs to them,
retire that tooling. The npm package doesn’t call them either.
Still stuck?
If nothing above matches, open an issue on the ProBot repo with:- The exact error text (from browser console + Network tab response body).
- A minimal repro: the
<ProbotBot />prop set (redact tokens) + yoursendMessageimplementation + your server proxy route. - Your framework + version (Next.js / Vite / plain HTML) + Node version.