Skip to content

Explain or refuse the errors that kept connectors stuck - #1437

Merged
helpcodeai merged 5 commits into
mainfrom
fix/stuck-connector-errors
Nov 2, 2026
Merged

helpcodeai merged 5 commits into
mainfrom
fix/stuck-connector-errors

Conversation

@helpcodeai

Copy link
Copy Markdown
Contributor

Summary

The Cloud weekly stuck-users report lists connectors that fail every call with an error that does not say what to fix: a model hits a Gemini model without a free tier on its first call, an Odoo address saved with /web after the host, a MySQL host left as the literal host, a key pasted three times, an eBay connector with no refresh token sending an empty bearer. This PR either catches these when the connector is set up or edited, or answers the failing call with what the user has to change and where.

Most of it reaches installed connectors without "Apply update": call-time hints live in code (error-hints.ts), and the adapters' verifyHints, probe and envVarMeta are read from the catalog when used. No endpointMapping changed.

Changes

Call-time hints (packages/backend/src/mcp-server/error-hints.ts)

  • Gemini: separate hints for 429 with limit: 0 on a free_tier metric (the model has no free tier and the project has no billing, so use gemini-3.1-flash-lite or enable billing), any other 429 (rate limit, wait once), and 402 (prepaid credits used up, top up in AI Studio). The 402 body also says RESOURCE_EXHAUSTED and used to get the quota hint.
  • Odoo JSON-2: 404 "No database is selected" now points at ODOO_DB and at a path after the host (/web, /odoo) in ODOO_URL. A 401 "User not authenticated, use an API key" (no key sent) now gets its own hint, separate from "Invalid apikey" (expired key).
  • Etsy 404 "Could not find a shop for User": authorize again with the Etsy account that owns the shop.
  • Zoho CRM NO_PERMISSION / Crm_Implied_Api_Access: the Free edition has no API access, and on paid editions the profile needs API Access.
  • Bitrix24 HTML 404: the URL is not an inbound webhook (a portal page) or the webhook was deleted. Keyed on *.bitrix24.* hosts or the bitrix24 adapter, so Bitrix24 boxes on their own domain are covered too.
  • Companies House "Invalid Authorization header": use a REST key (not a streaming key) of a Live application. The adapter's Basic auth was already correct (key as username, empty password; rest.engine.ts BASIC_AUTH).
  • Google "API key not valid" on *.googleapis.com (YouTube Data and similar).
  • TallyPrime / JTL-Wawi unreachable (ECONNABORTED, ETIMEDOUT, …). On Cloud the hint says the host must be reachable from the internet (a tunnel such as Cloudflare Tunnel, or port forwarding with HTTPS), or AnythingMCP must be self-hosted next to it. On self-hosted it says to check the network path from this server. An ngrok error page (seen on a TallyPrime 404) is read as "the tunnel is offline".
  • A 401 that no rule covers falls back to the adapter's own verifyHints["401"] / auth_failed. Credential hints end with the connector's page URL (connectorPageUrl), so a connector saved without a passing check hears the setup advice at its first failing call.
  • dynamic-mcp-tools.ts passes the adapter slug, its verifyHints, the page URL and the deployment mode. For Odoo "No database is selected" it also makes one unauthenticated request that lists the databases at that address (odooNoDatabaseAdvice, reusing the Name the right Odoo database when the entered one does not exist #931 lookup) and names a path after the host.

Refused at setup and on save (packages/backend/src/adapters/env-var-checks.ts, new)

  • Field-level refusal of placeholder values in address and credential fields: host, hostname, server, your-host, yourdomain, bare example.com / www.example.com, your-api-key, PASTE_KEY_HERE, <...>, TODO, changeme, xxx. The rules are deliberately narrow: subdomains of example.com, single-label hosts such as erp, localhost and IPs are accepted.
  • A credential that is the same string repeated 2+ times (unit of at least 8 characters) is refused with "This looks like the key pasted more than once. Paste it once." Seen as a 3×40-character Odoo key and a 2×192-character Atlassian token. atlassian.json is not touched.
  • The adapter's envVarMeta.pattern is now enforced on the server, not only in the setup form and the chat install.
  • Applied in verifyCredentials (returns fields, which the setup page shows under each field), importAdapter ("Save anyway" and chat installs, 400 invalid_values with fields), PUT /connectors/:id/env-vars and PUT /connectors/:id. The editors check only values the request changes, so a stored bad value never blocks an unrelated edit. Backup import (restore) is left alone.

OAuth connector without a token (oauth2-token.service.ts)

  • The eBay Sell connector in the report has no browser sign-in in the catalog (the refresh token is pasted) and was saved without one. It sent Authorization: Bearer 11 times and got eBay's "Missing access token". Now, when the refresh-token grant has an empty refreshToken, no stored token and no authorizationUrl, the call stops before any request with "no access token and no refresh token yet: open and enter the refresh token". Connectors with a browser sign-in already got "not authorized yet: open and click Authorize" (setupStatus === 'needs_authorization' and getAccessToken), so that flow is unchanged. The existing spec that pins an empty token for a connector with no refreshToken field still passes.

Adapters

  • youtube-data: probe (videoCategories, part=snippet, 1 quota unit) and verifyHints for 400 and 403. Why the YouTube connectors were not caught at setup: the adapter had no GET without required parameters, so pickProbe found nothing, verify returned skipped: no_probe, and the setup completed without any check. None of the 3 failing connectors had setup_saved_unverified or setup_verify_failed.
  • companies-house: the same root cause (no probe). Adds a probe (search with 1 result), verifyHints 400/401, and envVarMeta with link, help and a lenient pattern (no spaces or colon, at least 20 characters). The instructions now say to pick a Live application. I did not add a strict UUID pattern because I could not confirm that every valid Companies House key is a UUID.
  • bitrix24: envVarMeta pattern requiring …/rest/<user_id>/<code>/, with a patternMessage pointing to Developer resources → Other → Inbound webhook. Also a probe (scope) and verifyHints 401/404. The failing connector held a CRM kanban URL.
  • tallyprime, jtl-wawi: verifyHints.unreachable mention a tunnel or port forwarding on Cloud and a self-hosted install.
  • Telegram: the call-time rule already existed (TELEGRAM_BAD_TOKEN_HINT, and the adapter has a probe). Only 2 of the failing workspaces had setup_saved_unverified. These connectors now also get the connector-page link.

Docs: docs/tool-definition.md now covers where patterns and placeholder checks apply, call-time use of verifyHints, and when to declare a probe.

Behaviour on Cloud and self-hosted

  • Hints, field checks, the OAuth stop and the new probes behave the same in both modes.
  • The only mode-dependent text is the TallyPrime / JTL-Wawi "unreachable" hint. With DEPLOYMENT_MODE=cloud it asks for an internet-reachable address (tunnel or port forwarding) or a self-hosted install; self-hosted (the default) asks to check the network path from this server.
  • The connector-page link uses FRONTEND_URL, as connectorPageUrl already does. Without it, no link is added.

Gemini default model

Per https://ai.google.dev/gemini-api/docs/pricing (checked today), gemini-3.1-flash-lite (the default text model) is free of charge on the Standard tier. Every 429 with limit: 0 in the last 21 days came from models without a free tier: gemini-3.1-pro-preview and the image models (gemini-3.1-flash-lite-image, gemini-3.1-flash-image, gemini-3-pro-image, gemini-nano-banana-2.1). A generate call in the setup check would therefore pass for these users and catch nothing, so I left the probe as gemini_list_models. The fix is the distinct hint, which names the free text model and billing.

Not done / needs a larger design

  • No call-time Zoho or Etsy lookup: hint only.
  • Hand-built (non-catalog) DATABASE connectors get no placeholder check. They have no adapter metadata to tell an address field from a setting. The MySQL case in the report is covered when the connector comes from the catalog adapter.
  • The existing Etsy / Google Search Console verify specs now use pattern-valid app keys (ks/ss and id failed the adapters' own patterns, which the server now enforces).

Type

  • Bug fix
  • New feature
  • Documentation
  • Refactoring
  • Performance improvement
  • Other (describe below)

Testing

  • Added/updated unit tests
  • Tested manually (describe steps below)
  • No tests needed (explain why)

Commands run (Node from the repo's lockfile, npm ci --ignore-scripts at the root, npx prisma generate in packages/backend):

Working directory Command Result
repo root node scripts/regenerate-catalog.mjs 705 adapters, no catalog.ts diff (it imports the JSON files)
repo root node scripts/validate-adapters.mjs --warn exit 0; the only warnings on touched adapters (tool prefix and description length on youtube-data and bitrix24) existed before
packages/backend npx tsc --noEmit -p tsconfig.json exit 0
packages/backend npm run lint 0 errors, 11 warnings, none in touched files; no unrelated --fix rewrites
packages/backend npx jest --maxWorkers=2 on the 21 touched or covering spec files: env-var-checks, error-hints, error-hints.stuck-connectors, odoo-database-hint, oauth2-token.service, adapters.service, connectors.controller, connector-setup.service, adapters.controller, dynamic-mcp-tools, dynamic-mcp-tools.per-user, secret-guard.flows, catalog, catalog-search, env-var-meta, connector-setup-status.util, and the static specs of jtl-wawi, tallyprime, bitrix24, odoo, youtube-data 21 suites passed, 15383 tests passed, 2 skipped (live calls)
packages/frontend npx tsc --noEmit -p tsconfig.json exit 0
packages/frontend npm run lint 0 errors, 49 warnings, none in touched files

The full backend suite was not run, to keep the shared machine's load down. No Playwright run: the frontend change is 5 lines on the setup page that show the server's field messages.

Checklist

  • My code follows the existing code style
  • I have tested my changes locally
  • I have updated documentation (if applicable)
  • All existing tests pass (npm test): only the targeted specs above were run
  • This PR has a descriptive title

Related Issues

From the AnythingMCP Cloud weekly stuck-users report; no public issue.

From the Cloud weekly stuck-users report: connectors that failed every
call with an error that did not say what to fix.

- Hints for Gemini 429 without a free tier (limit 0) vs a rate limit,
  402 prepaid credits, Odoo JSON-2 "No database is selected" and
  "User not authenticated", Etsy account without a shop, Zoho CRM
  without API access, Bitrix24 HTML 404, Companies House key, Google
  "API key not valid", unreachable TallyPrime / JTL-Wawi (Cloud vs
  self-hosted) and an offline tunnel.
- A 401 no rule covers repeats the adapter's verifyHints, and
  credential hints link the connector's page.
- Odoo "No database is selected" names a path after the host and the
  databases the address serves.
- Refuse placeholder values, values the adapter pattern refuses and
  keys pasted twice at verify, install and in the variable editors.
- Do not send an empty bearer when a pasted refresh token is missing.
- Probes for YouTube Data, Companies House and Bitrix24, so a wrong
  key fails at setup; Bitrix24 webhook URL pattern.
The server now enforces adapter patterns; some working connectors hold
values in a shape that means one thing but missed the pattern:
- telegram-bot: strip whitespace from TELEGRAM_BOT_TOKEN.
- bitrix24: cut a method name or query after /rest/<id>/<code>/.
- digisac: accept accounts on .digisac.io.
- substack: keep refusing a profile link, saying a profile is not a
  publication.
Specs cover each case, the weclapp host-to-tenant normalisation and
stored Etsy values that an unrelated edit leaves alone.
@helpcodeai
helpcodeai force-pushed the fix/stuck-connector-errors branch from a5b5092 to 3be0b42 Compare November 2, 2026 09:31
@helpcodeai
helpcodeai enabled auto-merge (squash) November 2, 2026 09:46
@helpcodeai
helpcodeai merged commit ba0fb2e into main Nov 2, 2026
14 checks passed
@helpcodeai
helpcodeai deleted the fix/stuck-connector-errors branch November 2, 2026 10:02
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant