{"record":{"id":"9e5b36b338f60cfc","repo":"nexu-io/open-design","slug":"connector-tool-not-found","errorCode":"CONNECTOR_TOOL_NOT_FOUND","errorMessage":"connector tool is not allowed","messagePattern":"connector tool is not allowed","errorType":"exception","errorClass":"ConnectorServiceError","httpStatus":404,"severity":"error","filePath":"apps/daemon/src/connectors/service.ts","lineNumber":784,"sourceCode":"    const connector = this.toDetail(definition);\n    if (connector.status === 'disabled') {\n      throw new ConnectorServiceError('CONNECTOR_DISABLED', 'connector is disabled', 403);\n    }\n    if (connector.status !== 'connected') {\n      throw new ConnectorServiceError('CONNECTOR_NOT_CONNECTED', 'connector is not connected', 403, {\n        connectorId: request.connectorId,\n        status: connector.status,\n      });\n    }\n    if (request.expectedAccountLabel !== undefined && connector.accountLabel !== request.expectedAccountLabel) {\n      throw new ConnectorServiceError('CONNECTOR_NOT_CONNECTED', 'connector account changed since refresh approval', 409, {\n        connectorId: request.connectorId,\n        expectedAccountLabel: request.expectedAccountLabel,\n        currentAccountLabel: connector.accountLabel ?? null,\n      });\n    }\n    if (!definition.allowedToolNames.includes(request.toolName)) {\n      throw new ConnectorServiceError('CONNECTOR_TOOL_NOT_FOUND', 'connector tool is not allowed', 404, {\n        connectorId: request.connectorId,\n        toolName: request.toolName,\n      });\n    }\n    const tool = definition.tools.find((candidate) => candidate.name === request.toolName);\n    if (!tool) {\n      throw new ConnectorServiceError('CONNECTOR_TOOL_NOT_FOUND', 'connector tool not found', 404);\n    }\n    const runtimeSafety = runtimeSafetyForTool(tool);\n    const effectiveApproval = stricterApproval(stricterApproval(definition.minimumApproval, tool.safety.approval), runtimeSafety.approval);\n    if (effectiveApproval !== 'auto' || runtimeSafety.sideEffect !== 'read') {\n      throw new ConnectorServiceError('CONNECTOR_SAFETY_DENIED', 'connector tool is not auto-approved read-only by current safety policy', 403, {\n        connectorId: request.connectorId,\n        toolName: request.toolName,\n        approvalPolicy: effectiveApproval ?? null,\n        safety: { ...runtimeSafety },\n      });\n    }","sourceCodeStart":766,"sourceCodeEnd":802,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/connectors/service.ts#L766-L802","documentation":"Thrown by ConnectorService.execute() with HTTP 404 when request.toolName is not present in definition.allowedToolNames — the catalog whitelist for that connector. This is checked after the connection gate but before the per-tool lookup, so the connector is reachable but the tool is not enabled for it. details includes connectorId and toolName.","triggerScenarios":"Calling execute() with a toolName that the connector's catalog definition does not allow — either a typo, a tool removed from the connector manifest, or a connector whose allowedToolNames was pruned by admin policy.","commonSituations":"Agent hallucinated a tool name; the connector catalog was refreshed and the tool was dropped from allowedToolNames; an environment-specific manifest restricts which tools a connector exposes.","solutions":["List the connector's allowed tools (connectorService list / od connector tools) and call one of those names instead.","If the tool should be allowed, update the connector catalog definition to include it in allowedToolNames before retrying.","Check for typos / casing — toolName must match exactly (the lookup is case-sensitive)."],"exampleFix":"// before: guessing a tool name\nawait execute({ connectorId, toolName: 'gmail_send', input }, ctx);\n\n// after: pick from the live allowed list\nconst allowed = connectorService.getAllowedToolNames(connectorId);\nconst toolName = allowed.find(t => t.startsWith('gmail'));\nif (!toolName) throw new Error('no gmail tool available');\nawait execute({ connectorId, toolName, input }, ctx);","handlingStrategy":"validation","validationCode":"const def = connectorService.getDefinition(request.connectorId);\nif (!def?.allowedToolNames.includes(request.toolName)) {\n  throw new Error(`tool ${request.toolName} not allowed for ${request.connectorId}`);\n}\nawait connectorService.execute(request, context);","typeGuard":"function isAllowedTool(def: ConnectorCatalogDefinition | undefined, name: string): boolean {\n  return !!def && def.allowedToolNames.includes(name);\n}","tryCatchPattern":"try { await connectorService.execute(request, context); }\ncatch (e) {\n  if (e instanceof ConnectorServiceError && e.code === 'CONNECTOR_TOOL_NOT_FOUND' && e.status === 404) {\n    // fetch allowedToolNames, pick a substitute, or surface 'tool unavailable' to the agent\n  } else throw e;\n}","preventionTips":["Expose the connector's allowedToolNames to the agent so it only proposes valid tools.","Refresh the catalog after connector updates so stale tool names are not retained.","Validate toolName against the live catalog at the request boundary."],"tags":["connector","validation","http-404","catalog"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}