{"record":{"id":"7985a3f54ffed0fe","repo":"nexu-io/open-design","slug":"connector-execution-failed-7985a3","errorCode":"CONNECTOR_EXECUTION_FAILED","errorMessage":"callbackUrl is required for Composio connectors","messagePattern":"callbackUrl is required for Composio connectors","errorType":"exception","errorClass":"ConnectorServiceError","httpStatus":400,"severity":"error","filePath":"apps/daemon/src/connectors/service.ts","lineNumber":691,"sourceCode":"        return;\n      }\n      results[connectorId] = await composioConnectorProvider.prepareAuthConfig(definition, signal);\n    }));\n\n    return { results };\n  }\n\n  async connect(connectorId: string, options: { accountLabel?: string; credentials?: ConnectorCredentialMaterial; callbackUrl?: string; signal?: AbortSignal } = {}): Promise<ConnectorConnectResult> {\n    const definition = this.getFastDefinition(connectorId) ?? await this.getDefinition(connectorId, options.signal);\n    if (!definition) {\n      throw new ConnectorServiceError('CONNECTOR_NOT_FOUND', 'connector not found', 404);\n    }\n\n    let auth: ComposioConnectionStart | undefined;\n    let detailDefinition = definition;\n    if (definition.authentication === 'composio' && options.credentials === undefined) {\n      if (!options.callbackUrl) {\n        throw new ConnectorServiceError('CONNECTOR_EXECUTION_FAILED', 'callbackUrl is required for Composio connectors', 400, { connectorId });\n      }\n      auth = await composioConnectorProvider.connect(definition, options.callbackUrl, options.signal);\n      if (auth.kind === 'redirect_required' || auth.kind === 'pending') {\n        return { connector: this.toDetail(detailDefinition), auth: publicComposioAuthStart(auth) };\n      }\n      if (auth.credentials !== undefined) {\n        options = { ...options, ...(auth.accountLabel === undefined ? {} : { accountLabel: auth.accountLabel }), credentials: auth.credentials };\n      }\n    }\n\n    const status = this.statusService.connect(detailDefinition, options.accountLabel, options.credentials);\n    if (status.status === 'disabled') {\n      throw new ConnectorServiceError('CONNECTOR_DISABLED', 'connector is disabled', 403);\n    }\n    return { connector: this.toDetail(detailDefinition), ...(auth === undefined ? {} : { auth: publicComposioAuthStart(auth) }) };\n  }\n\n  async disconnect(connectorId: string): Promise<ConnectorDetail> {","sourceCodeStart":673,"sourceCodeEnd":709,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/connectors/service.ts#L673-L709","documentation":"Thrown by ConnectorService.connect() when the resolved definition is Composio-backed (definition.authentication === 'composio'), no credentials were supplied (options.credentials === undefined), and options.callbackUrl is missing. The Composio OAuth redirect flow needs a callback URL to send the user back; without it the daemon cannot start the connection. ConnectorServiceError with code CONNECTOR_EXECUTION_FAILED and HTTP 400.","triggerScenarios":"Initiating a Composio OAuth connection without supplying credentials and without a callbackUrl, e.g. a CLI/agent call that calls connect() for an OAuth connector but has no HTTP endpoint to receive the redirect.","commonSituations":"CLI or headless agent tries to start an OAuth flow it cannot complete; the web route forgot to forward its callback URL; migration to a new callback host left the URL empty.","solutions":["Supply options.callbackUrl (a publicly/headlessly reachable URL that completes the OAuth round trip) when calling connect() for a Composio connector.","If you already hold credentials, pass options.credentials to skip the OAuth redirect path entirely.","For CLI/headless use, prefer pre-obtained credentials or an out-of-band OAuth flow rather than connect()."],"exampleFix":"// before\nawait connectorService.connect('composio-github', {}); // no callbackUrl, no credentials\n// throws \"callbackUrl is required for Composio connectors\"\n\n// after\nawait connectorService.connect('composio-github', {\n  callbackUrl: 'https://app.example.com/api/connectors/callback',\n});","handlingStrategy":"validation","validationCode":"async function connectComposio(connectorService: ConnectorService, id: string, opts: { callbackUrl?: string; credentials?: Record<string, unknown> }) {\n  const detail = await connectorService.getConnector(id);\n  const isComposio = detail.auth?.flow === 'composio'; // or inspect the definition's authentication\n  if (isComposio && !opts.credentials && !opts.callbackUrl) {\n    throw new Error('callbackUrl is required to start a Composio OAuth flow');\n  }\n  return connectorService.connect(id, opts);\n}","typeGuard":"function hasUsableComposioConnectOptions(opts: { callbackUrl?: string; credentials?: unknown }): boolean {\n  return opts.credentials !== undefined || typeof opts.callbackUrl === 'string' && opts.callbackUrl.length > 0;\n}\n\n// if (!hasUsableComposioConnectOptions(opts)) throw new Error('provide callbackUrl or credentials');","tryCatchPattern":"try {\n  return await connectorService.connect(id, opts);\n} catch (error) {\n  if (error instanceof ConnectorServiceError\n      && error.code === 'CONNECTOR_EXECUTION_FAILED'\n      && /callbackUrl is required/.test(error.message)) {\n    return { ok: false, reason: 'missing_callback_url' };\n  }\n  throw error;\n}","preventionTips":["For Composio OAuth connectors, always provide a reachable callbackUrl (or pre-obtained credentials).","For headless/CLI use, supply credentials directly to bypass the redirect path.","Validate callbackUrl reachability before starting the flow."],"tags":["connector","composio","oauth","callback","connect"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}