{"record":{"id":"bca72236fd0ac109","repo":"danny-avila/LibreChat","slug":"invalid-action-error","errorCode":"invalid_action_error","errorMessage":"{\"type\":\"invalid_action_error\"}","messagePattern":"\\{\"type\":\"invalid_action_error\"\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"api/server/services/ToolService.js","lineNumber":466,"sourceCode":"\n      // We've already decrypted the metadata, so we can pass it directly\n      const _allowedDomains = appConfig?.actions?.allowedDomains;\n      const _allowedAddresses = appConfig?.actions?.allowedAddresses;\n      tool = await createActionTool({\n        userId: client.req.user.id,\n        res: client.res,\n        action,\n        requestBuilder,\n        // Note: intentionally not passing zodSchema, name, and description for assistants API\n        encrypted, // Pass the encrypted values for OAuth flow\n        useSSRFProtection: !Array.isArray(_allowedDomains) || _allowedDomains.length === 0,\n        allowedAddresses: _allowedAddresses,\n      });\n      if (!tool) {\n        logger.warn(\n          `Invalid action: user: ${client.req.user.id} | thread_id: ${requiredActions[0].thread_id} | run_id: ${requiredActions[0].run_id} | toolName: ${currentAction.tool}`,\n        );\n        throw new Error(`{\"type\":\"${ErrorTypes.INVALID_ACTION}\"}`);\n      }\n      isActionTool = !!tool;\n      ActionToolMap[currentAction.tool] = tool;\n    }\n\n    if (currentAction.tool === 'calculator') {\n      currentAction.toolInput = currentAction.toolInput.input;\n    }\n\n    const handleToolError = (error) => {\n      logger.error(\n        `tool_call_id: ${currentAction.toolCallId} | Error processing tool ${currentAction.tool}`,\n        error,\n      );\n      return {\n        tool_call_id: currentAction.toolCallId,\n        output: `Error processing tool ${currentAction.tool}: ${redactMessage(error.message, 256)}`,\n      };","sourceCodeStart":448,"sourceCodeEnd":484,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/ToolService.js#L448-L484","documentation":"Thrown by the Assistants-API action-tool path in ToolService when createActionTool(...) returns a falsy value for a required action. The thrown payload is a JSON string {\"type\":\"invalid_action_error\"} (ErrorTypes.INVALID_ACTION) so downstream handlers can detect it by type. A null tool means the action could not be constructed — typically missing metadata, failed OAuth decryption, or SSRF/address-policy rejection.","triggerScenarios":"An assistant run submits a required_action for a tool whose action definition was deleted or never fully configured; the action's encrypted OAuth values cannot be decrypted; useSSRFProtection blocked the action's domain because allowedDomains is empty and the action URL is non-allowlisted; the requestBuilder or action payload is incomplete.","commonSituations":"User deleted an action after the run started; OAuth credentials expired and re-encryption produced an unreadable value; an admin tightened actions.allowedDomains/allowedAddresses so a previously-working action is now blocked.","solutions":["Re-open the action configuration in the UI and confirm all required fields (endpoint, auth, schema) are present and saved.","If using OAuth, complete the OAuth flow again so fresh encrypted values are stored.","Review appConfig.actions.allowedDomains/allowedAddresses and add the action's domain, or disable useSSRFProtection intentionally by configuring allowedDomains.","Check the preceding logger.warn line which records user/thread/run/toolName — it identifies exactly which action failed to build."],"exampleFix":"// before (no guard, raw error bubbles to the run)\nconst tool = await createActionTool({ ... });\n\n// after (caller)\nif (!tool) {\n  return { output: `Action '${currentAction.tool}' is misconfigured. Please re-authorize it.` };\n}","handlingStrategy":"try-catch","validationCode":"const entry = actionSetsData.get(normalizeActionToolName(currentAction.tool));\nif (!entry) {\n  return { output: `Action '${currentAction.tool}' is not available.` };\n}","typeGuard":"const isActionConfigured = (actionSetsData, tool) => actionSetsData.has(normalizeActionToolName(tool));","tryCatchPattern":"try {\n  // ... build and run action tool ...\n} catch (err) {\n  if (err.message.includes('invalid_action_error')) {\n    return { output: `Action '${currentAction.tool}' is misconfigured. Re-authorize it.` };\n  }\n  throw err;\n}","preventionTips":["Keep action definitions and their OAuth credentials current.","Configure appConfig.actions.allowedDomains/allowedAddresses deliberately.","Audit the warn log line naming user/thread/run/toolName to catch misconfigurations early."],"tags":["tools","actions","assistants","config","ssrf"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}