danny-avila/LibreChat · error · Error

invalid_action_error

invalid_action_error

Error message

{"type":"invalid_action_error"}

What it means

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.

Source

Thrown at api/server/services/ToolService.js:466

      // We've already decrypted the metadata, so we can pass it directly
      const _allowedDomains = appConfig?.actions?.allowedDomains;
      const _allowedAddresses = appConfig?.actions?.allowedAddresses;
      tool = await createActionTool({
        userId: client.req.user.id,
        res: client.res,
        action,
        requestBuilder,
        // Note: intentionally not passing zodSchema, name, and description for assistants API
        encrypted, // Pass the encrypted values for OAuth flow
        useSSRFProtection: !Array.isArray(_allowedDomains) || _allowedDomains.length === 0,
        allowedAddresses: _allowedAddresses,
      });
      if (!tool) {
        logger.warn(
          `Invalid action: user: ${client.req.user.id} | thread_id: ${requiredActions[0].thread_id} | run_id: ${requiredActions[0].run_id} | toolName: ${currentAction.tool}`,
        );
        throw new Error(`{"type":"${ErrorTypes.INVALID_ACTION}"}`);
      }
      isActionTool = !!tool;
      ActionToolMap[currentAction.tool] = tool;
    }

    if (currentAction.tool === 'calculator') {
      currentAction.toolInput = currentAction.toolInput.input;
    }

    const handleToolError = (error) => {
      logger.error(
        `tool_call_id: ${currentAction.toolCallId} | Error processing tool ${currentAction.tool}`,
        error,
      );
      return {
        tool_call_id: currentAction.toolCallId,
        output: `Error processing tool ${currentAction.tool}: ${redactMessage(error.message, 256)}`,
      };

View on GitHub (pinned to 5ff282f900)

Solutions

  1. Re-open the action configuration in the UI and confirm all required fields (endpoint, auth, schema) are present and saved.
  2. If using OAuth, complete the OAuth flow again so fresh encrypted values are stored.
  3. Review appConfig.actions.allowedDomains/allowedAddresses and add the action's domain, or disable useSSRFProtection intentionally by configuring allowedDomains.
  4. Check the preceding logger.warn line which records user/thread/run/toolName — it identifies exactly which action failed to build.

Example fix

// before (no guard, raw error bubbles to the run)
const tool = await createActionTool({ ... });

// after (caller)
if (!tool) {
  return { output: `Action '${currentAction.tool}' is misconfigured. Please re-authorize it.` };
}
Defensive patterns

Strategy: try-catch

Validate before calling

const entry = actionSetsData.get(normalizeActionToolName(currentAction.tool));
if (!entry) {
  return { output: `Action '${currentAction.tool}' is not available.` };
}

Type guard

const isActionConfigured = (actionSetsData, tool) => actionSetsData.has(normalizeActionToolName(tool));

Try / catch

try {
  // ... build and run action tool ...
} catch (err) {
  if (err.message.includes('invalid_action_error')) {
    return { output: `Action '${currentAction.tool}' is misconfigured. Re-authorize it.` };
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12). Data as JSON: /api/errors/bca72236fd0ac109. Report an issue: GitHub.