-
Notifications
You must be signed in to change notification settings - Fork 0
Handle Composio connect 4xx errors #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,49 +1,180 @@ | ||
| # OpenClaws.me - Architecture | ||
| # OpenClaws — Current Architecture | ||
|
|
||
| ## Overview | ||
| Hosted OpenClaw agent platform. Users sign up, get an AI agent on Telegram/Discord/WhatsApp, connect integrations via Composio OAuth. | ||
|
|
||
| OpenClaws is a hosted personal-agent platform. A user signs in on `openclaws.biz`, starts a dedicated OpenClaw gateway, manages provider keys and channels from the dashboard, and reaches that agent through the hosted web app plus paired messaging surfaces. | ||
|
|
||
| This document describes the **current implementation**, not the original Railway-era plan. | ||
|
|
||
| ## Stack | ||
| - **Framework:** Next.js 15 (App Router) | ||
| - **Hosting:** Vercel | ||
|
|
||
| - **Framework:** Next.js 16 (App Router) | ||
| - **Hosting:** Vercel for the web app | ||
| - **Auth:** NextAuth.js (Google OAuth) | ||
| - **Database:** Supabase (PostgreSQL) | ||
| - **Billing:** Stripe Checkout + Customer Portal | ||
| - **Agent Provisioning:** Railway API (Docker containers) | ||
| - **Integrations:** Composio SDK | ||
| - **Styling:** Tailwind CSS | ||
|
|
||
| ## Pages | ||
| - `/` - Landing page (already built, copy from openclaws-landing/) | ||
| - `/login` - Google OAuth sign-in | ||
| - `/dashboard` - Main chat interface (like TrustClaw) | ||
| - `/dashboard/toolkits` - Composio integrations (like TrustClaw toolkits) | ||
| - `/dashboard/settings` - User settings, billing, model selection | ||
| - `/api/auth/[...nextauth]` - NextAuth routes | ||
| - `/api/stripe/webhook` - Stripe webhook handler | ||
| - `/api/stripe/checkout` - Create checkout session | ||
| - `/api/agent/provision` - Provision OpenClaw instance on Railway | ||
|
|
||
| ## User Flow | ||
| 1. Land on openclaws.me | ||
| 2. Click "Get Started" -> Google OAuth | ||
| 3. Free tier: basic chat, limited messages | ||
| 4. Click "Upgrade" -> Stripe $29/mo | ||
| 5. Premium: better models, unlimited, toolkits access | ||
| 6. Toolkits: connect Gmail, GitHub, Slack etc. via Composio OAuth | ||
|
|
||
| ## Agent Provisioning | ||
| Each user gets a dedicated OpenClaw Docker container on Railway: | ||
| - Docker image: ghcr.io/openclaw/openclaw:latest | ||
| - Configured with user's chosen model + channel | ||
| - Composio integrations passed as env vars | ||
| - Health monitoring via Railway API | ||
|
|
||
| ## Free vs Premium | ||
| | Feature | Free | Premium ($29/mo) | | ||
| |---------|------|-------------------| | ||
| | Messages | 50/day | Unlimited | | ||
| | Model | Sonnet 4.5 | Opus 4.5 + GPT-4o + Gemini | | ||
| | Integrations | 3 | Unlimited | | ||
| | Channels | 1 | All (Telegram, Discord, WhatsApp) | | ||
| | Mission Control | Basic | Full dashboard | | ||
| - **Billing:** Stripe | ||
| - **Gateway provisioning:** Fly Machines | ||
| - **Observability:** Sentry web + server SDKs, Sentry webhook -> Linear + Paperclip issue creation | ||
| - **Agent runtime:** Dedicated OpenClaw gateway per customer | ||
|
|
||
| ## Main Product Surfaces | ||
|
|
||
| - `/` - marketing / landing page | ||
| - `/login` - Google sign-in | ||
| - `/dashboard` - primary app shell | ||
| - `/dashboard/settings` - model selection, provider keys, billing, personalization | ||
| - `/dashboard/toolkits` - Composio-connected integrations | ||
| - `/api/instance` - create, fetch, and destroy a customer gateway instance | ||
| - `/api/gateway/*` - gateway control-plane actions used by the dashboard | ||
| - `/api/webhooks/sentry` - receives Sentry issue webhooks and opens bugs in Linear and Paperclip | ||
|
|
||
| ## Provisioning Flow | ||
|
|
||
| Gateway provisioning is handled in `src/lib/fly.ts` and invoked from `src/app/api/instance/route.ts`. | ||
|
|
||
| ### What gets created | ||
|
|
||
| For each provisioned customer: | ||
|
|
||
| 1. A Fly app named from `FLY_APP_PREFIX` + a unique slug | ||
| 2. A persistent Fly volume mounted at `/data` | ||
| 3. A dedicated Fly Machine running the OpenClaw gateway image | ||
| 4. A custom subdomain at `https://{slug}.openclaws.biz` | ||
| 5. A generated gateway token stored in the OpenClaws database | ||
|
|
||
| ### Runtime image and startup behavior | ||
|
|
||
| The machine currently runs: | ||
|
|
||
| - Image: `ghcr.io/openclaw/openclaw:main` | ||
| - Command path: `node dist/index.js gateway --allow-unconfigured --port 3000 --bind lan` | ||
|
|
||
| Before boot, provisioning seeds `/data/openclaw.json` and writes an `IDENTITY.md` file so the hosted agent introduces itself as an OpenClaws agent rather than a raw upstream model persona. | ||
|
|
||
| ### Gateway config seeded at provision time | ||
|
|
||
| Provisioning currently sets: | ||
|
|
||
| - `gateway.controlUi.allowedOrigins` for the hosted domains | ||
| - `gateway.controlUi.allowInsecureAuth = false` | ||
| - `skills.entries.composio.enabled = true` | ||
| - a minimal default `agents.list` entry for the customer agent | ||
|
|
||
| ### Provider-key behavior | ||
|
|
||
| OpenClaws passes through user-supplied model keys when available: | ||
|
|
||
| - `ANTHROPIC_API_KEY` | ||
| - `ANTHROPIC_OAUTH_TOKEN` | ||
| - `OPENAI_API_KEY` | ||
|
|
||
| If the user has not supplied Anthropic credentials and the platform has one configured, OpenClaws mints a short-lived proxy token and points the gateway at the hosted Anthropic proxy. | ||
|
|
||
| ### Composio behavior | ||
|
|
||
| If Composio is configured, provisioning also: | ||
|
|
||
| - passes `COMPOSIO_API_KEY` | ||
| - passes a per-user `COMPOSIO_ENTITY_ID` | ||
| - installs a lightweight `composio-exec` bridge into the customer gateway | ||
| - installs a small Composio skill into persistent storage | ||
|
|
||
| ## Security / Network Posture | ||
|
|
||
| ### Current behavior | ||
|
|
||
| The provisioned gateway is intentionally exposed through Fly on ports 80/443 and the OpenClaw process binds with `--bind lan` so Fly’s proxy can reach it. | ||
|
|
||
| Provisioning also starts a background loop that repeatedly runs: | ||
|
|
||
| `openclaw devices approve --latest` | ||
|
|
||
| This means device pairing is currently optimized for low-friction hosted onboarding rather than strict manual approval. | ||
|
|
||
| ### Why this matters | ||
|
|
||
| This is a deliberate **managed-hosting exception** to the stricter internal OpenClaw posture of loopback-only listeners plus explicit device approval. | ||
|
|
||
| Operational upside: | ||
|
|
||
| - much simpler end-user onboarding | ||
| - fewer support tickets for first-time pairing | ||
| - works with Fly’s public routing model | ||
|
|
||
| Tradeoff / risk: | ||
|
|
||
| - weaker pairing friction than a manually approved device flow | ||
| - background approval can hide pairing-state problems instead of surfacing them clearly | ||
| - security expectations must be documented accurately so support and engineering do not assume loopback-only behavior | ||
|
|
||
| If OpenClaws later tightens posture, the likely path is: | ||
|
|
||
| - remove auto-approval loop | ||
| - keep hosted ingress but require explicit pairing approval | ||
| - make sleeping / waking / approval state visible in the dashboard | ||
|
|
||
| ## Pairing Approval Path | ||
|
|
||
| The dashboard can call `POST /api/gateway/approve-pairing`. | ||
|
|
||
| Current behavior: | ||
|
|
||
| - looks up the customer’s Fly app / machine / gateway token | ||
| - verifies the Fly machine is actually running | ||
| - executes `openclaw devices approve --latest --json` inside the machine | ||
| - returns a specific response when there is no pending request | ||
| - returns a specific response when the machine is stopped / sleeping instead of a generic failure | ||
|
|
||
| ### Operational caveat | ||
|
|
||
| This route still depends on Fly machine exec, so it is inherently more fragile than an in-process control-plane API. If Fly exec is degraded or the machine is asleep, approval can fail even though the underlying gateway config is fine. | ||
|
|
||
| ## Observability and Support | ||
|
|
||
| ### Sentry | ||
|
|
||
| OpenClaws initializes Sentry in both client and server contexts: | ||
|
|
||
| - `sentry.client.config.ts` | ||
| - `sentry.server.config.ts` | ||
|
|
||
| Current safeguards include: | ||
|
|
||
| - disabling default PII sending | ||
| - masking client-side inputs in session replay | ||
| - stripping provider API headers from server breadcrumbs | ||
| - scrubbing common API key patterns from client error messages | ||
|
|
||
| ### Sentry -> Linear + Paperclip | ||
|
|
||
| `/api/webhooks/sentry` verifies the Sentry HMAC signature and creates bug tickets in: | ||
|
|
||
| - **Linear** for product/project tracking | ||
| - **Paperclip** for agent assignment / triage | ||
|
|
||
| Routing is based on the Sentry project slug. For `openclaws`, bugs are routed into the OpenClaws Linear and Paperclip projects and assigned to the OpenClaws lead agent when configured. | ||
|
|
||
| ### Current support gap | ||
|
|
||
| OpenClaws is good at turning thrown errors into tickets, but thinner on customer-visible diagnostics for provisioning and pairing. Support still relies heavily on: | ||
|
|
||
| - Fly machine state | ||
| - Fly exec health | ||
| - Sentry traces/errors | ||
| - DB instance rows | ||
|
|
||
| The dashboard does not yet expose a rich, first-party support view for why onboarding or pairing failed. | ||
|
|
||
| ## Known Gaps / Follow-Up Work | ||
|
|
||
| 1. **Pairing approval should avoid shelling through Fly exec when possible.** | ||
| A first-class control endpoint on the gateway would be more reliable than exec-ing CLI commands into the machine. | ||
|
|
||
| 2. **Provisioning posture needs an explicit product decision.** | ||
| The current hosted setup favors onboarding convenience over strict manual device authorization. That should remain documented unless/until product changes it. | ||
|
|
||
| 3. **Support visibility is still thin.** | ||
| The dashboard should eventually expose machine state, last provisioning error, and pairing status more directly. | ||
|
|
||
| 4. **Keep this file honest.** | ||
| If provisioning or network posture changes, update this document in the same PR. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
composio-core npm package ComposioError class documentation💡 Result:
The composio-core npm package refers to
@composio/core(latest v0.6.6 as of March 2026), the TypeScript SDK for Composio platform. It includes a ComposioError class for handling SDK and API errors. ComposioError is the base exception class thrown by the SDK for issues like API failures, invalid requests, server errors, authentication problems, and more. It is used in error handling for backend API responses, custom errors via CEG (Composio Error Generator), and specific cases like SERVER_UNREACHABLE. Key details from sources: - Thrown in catch blocks, e.g., console.error('❌ Error:', error) where error is ComposioError instance. - Handles HTTP status codes (400 Bad Request, 429 Rate Limit, 5xx Server Errors) with structured responses including message, status, code, request_id. - Maps backend errors to SDK error codes (e.g., API_KEY_INVALID, SERVER_UNREACHABLE). - Used in functions like getCustomError, handleAllError, throwAPIError. - Example crash report: "ComposioError: undefined is not an object (evaluating 'predefinedError.message')" in v0.5.39, fixed in later versions. No public TypeDoc or detailed API docs for ComposioError found in official docs (https://docs.composio.dev/errors/error-handling focuses on API errors). Source code in GitHub repo ComposioHQ/composio (js/src/sdk/utils/error.ts or similar), introduced/enhanced in PRs like#812,#928. For usage: try { // SDK operations } catch (error) { if (error instanceof ComposioError) { console.error(error.message); // Human-readable error details } } Primary package is@composio/core(npm install@composio/core). Older/legacy composio-core exists but is deprecated.Citations:
🏁 Script executed:
Repository: Alex-Alaniz/openclaws
Length of output: 47
🌐 Web query:
ComposioError class structure statusCode property@composio/coreGitHub source💡 Result:
The ComposioError class exists in both Python and TypeScript SDKs of
@composio/core(ComposioHQ/composio GitHub repo). It is used to wrap API errors with structured information including HTTP status codes. In Python (composio package): from composio.exceptions import ComposioError. Used in error handling examples, e.g., except ComposioError as e. No explicit class definition found in searched files, but imported from composio.exceptions module. Likely defined there with properties for error details. In TypeScript (@composio/core): ComposioError class introduced in PR#812(js/src/sdk/utils/error.ts), with CEG (Composio Error Generator) for standardized handling. Throws ComposioError instances via CEG.throwCustomError, handleAllError, etc. Maps backend HTTP status codes (400, 401, 500, etc.) to SDK error codes via tables like API_TO_SDK_ERROR_CODE or BE_STATUS_CODE_TO_SDK_ERROR_CODES. The statusCode property: Official docs show API error responses have "status": HTTP status code (e.g., 400). SDK ComposioError extracts/embeds this from axiosError.response.status during error formatting (getAPIErrorDetails in formatter.ts). Confirmed in error handling PRs (#928,#1089,#1347,#838) and bug#1716referencing ComposioError construction from HTTP errors. No direct "statusCode" property shown in snippets, but effectively accessible via error.status or similar from parsed response. Repo structure: Python in /python/composio/, TS in /js/src/sdk/utils/errors/. Default branch: next. Latest activity 2026-03-25.Citations:
🏁 Script executed:
rg "getComposioErrorStatus" --type ts --type js -A 10Repository: Alex-Alaniz/openclaws
Length of output: 1957
🏁 Script executed:
Repository: Alex-Alaniz/openclaws
Length of output: 2444
🌐 Web query:
ComposioError metadata property structure@composio/coreGitHub source code💡 Result:
The ComposioError in
@composio/coreis a custom TypeScript error class introduced for standardized error handling in the Composio JavaScript/TypeScript SDK. It is defined in the SDK source code at js/src/sdk/utils/error.ts (or similar path based on repository structure). Key details from GitHub source code analysis (ComposioHQ/composio repository): - ComposioError provides helpful properties including: - name: Error type name (e.g., 'ComposioToolNotFoundError') - message: Human-readable error message - code: Error code from predefined constants (e.g., API_KEY_INVALID, SERVER_UNAVAILABLE) - It works alongside CEG (Composio Error Generator) class, which handles error creation, mapping backend HTTP status codes to SDK error codes, and throwing custom errors via methods like CEG.throwCustomError and CEG.handleError. - Backend API errors are structured as: { "error": { "message": string, "status": number (HTTP status), "request_id": string, "suggested_fix"?: string } } - No explicit 'metadata' property is directly mentioned in ComposioError itself in available snippets. However, error handling enhancements include detailed context like sessionId in SDK context, API request details, and backend response parsing (type, name, message from formatter.ts). Metadata-like details may be captured in error payloads for telemetry (exceptionHandler.ts) or backend responses. The structure emphasizes descriptive messages, type-safe errors, and integration with backend responses for better debugging. For exact current code, view js/src/sdk/utils/error.ts in the ComposioHQ/composio GitHub repo (latest at@composio/core@0.3.3+). Python SDK has analogous ComposioError with similar properties.Citations:
🏁 Script executed:
Repository: Alex-Alaniz/openclaws
Length of output: 47
🌐 Web query:
site:github.com/ComposioHQ/composio "error.ts" ComposioError class definition💡 Result:
The ComposioError class is defined in the Composio JavaScript/TypeScript SDK, specifically in the file js/src/sdk/error.ts (or a similar path like js/src/sdk/utils/error.ts based on references). It was introduced as part of enhanced error handling in PR
#812(merged Nov 2024), alongside the CEG (Composio Error Generator) class for standardized error management. The class is used across SDK modules like apps.ts, integrations.ts, etc., via CEG.handleError. Subsequent PRs (e.g.,#838,#928,#1089) updated error codes, messages, mappings (e.g., API_TO_SDK_ERROR_CODE), and handling functions like handleAllError and throwAPIError in error.ts. No full source code or exact class definition (properties, constructor, methods) is available in public search results or PR descriptions; only references confirm its existence and purpose in the Composio SDK for throwing structured errors with details like type, name, message, description, and possible fixes.Citations:
🏁 Script executed:
Repository: Alex-Alaniz/openclaws
Length of output: 347
🌐 Web query:
composio-core ComposioError properties structure statusCode status code npm types💡 Result:
The ComposioError class in the composio-core (or
@composio/core) TypeScript SDK is a custom error class used for standardized error handling. It is introduced and used in the JS/TS SDK source code located at paths like js/src/sdk/utils/errors/src/error.ts or similar, as referenced in multiple GitHub PRs. Key properties and structure, synthesized from error examples, GitHub issues, PRs, and docs: - errorCode or code: String identifier like "BACKEND::SERVER_ERROR" or "SERVER_UNREACHABLE" (used for programmatic handling). - message: Human-readable error description, e.g., "💥 Oops! Something went wrong on our end." or "Error Code: BACKEND::SERVER_ERROR". - status or statusCode: HTTP status code from backend response, e.g., 400, 500 (camelCase likely in TS types, matching API response "status": 400). - request_id: Unique request identifier for support. - suggested_fix: Optional guidance to resolve the error. - description: Additional details, sometimes from backend. - Stack Trace: Standard Error stack. Example from real usage (GitHub issue): ComposioError: Error Code: BACKEND::SERVER_ERROR Message: 💥 Oops! Something went wrong on our end. Description: "Cannot read properties of null (reading 'join')" in (URL: /api/v1/connectedAccounts, Status: 500) Suggested Fix: Please try again later. If the issue persists, contact support. Stack Trace: Error at new ComposioError The backend API returns errors as { "error": { "message": string, "status": number, "request_id": string, "suggested_fix"?: string } }, and the SDK wraps this into ComposioError instances using classes like CEG (Composio Error Generator) via methods like CEG.throwCustomError, handleError, throwAPIError. NPM package: composio-core (legacy, last ~1yr ago) or current@composio/core. No public types.d.ts snippet found in searches, but confirmed via GitHub source/PRs and error logs. Mintlify docs mention: name (error type like 'ComposioToolNotFoundError'), message. For full TypeScript interface, check the repo source: https://github.com/ComposioHQ/composio/tree/master/js/src/sdk/utils/errors (inferred path).Citations:
🏁 Script executed:
Repository: Alex-Alaniz/openclaws
Length of output: 2181
🌐 Web query:
"composio-core" OR "@composio/core" npm "statusCode" OR "status" error property direct💡 Result:
In the
@composio/corenpm package (the current core Composio SDK for TypeScript/Node.js), API errors from the Composio backend are returned as structured error objects that include a "status" property containing the HTTP status code (e.g., 400). These errors are typically thrown when making API calls like tools.get or tools.execute and caught in try-catch blocks, as shown in the official npm README examples: console.error('❌ Error:', error); The backend error format is: { "error": { "message": "description", "status": 400, "request_id": "...", "suggested_fix": "..." } } The SDK processes these into custom error instances with properties like message, name, type, and status accessible directly on the error object (error.status). This is confirmed by official docs on error handling and multiple GitHub PRs improving SDK error parsing for backend responses, including handling status codes like 400/404 in files like error.ts, formatter.ts, and constants.ts. Legacy package "composio-core" (older versions) exists but is not current; use@composio/core(v0.6+ as of 2026).Citations:
🏁 Script executed:
Repository: Alex-Alaniz/openclaws
Length of output: 508
The property path
error.metadata.statusCodeis not documented and likely incorrect.ComposioErroris the correct class name. However, based on the Composio SDK documentation and error structure, the status code should be accessed directly aserror.statusorerror.statusCode, not through ametadataobject. The backend error format showsstatusas a top-level property, and all documentation indicates direct property access.The inconsistency is evident in the same file:
getComposioErrorMessagecorrectly accesseserror.messageanderror.descriptiondirectly, whilegetComposioErrorStatususes the undocumentederror.metadata.statusCodepath with defensive type casting.Update
getComposioErrorStatusto:🤖 Prompt for AI Agents