danny-avila/LibreChat · error · Error

Decryption failed for plugin ${pluginKey}, field ${auth.auth

Error message

Decryption failed for plugin ${pluginKey}, field ${auth.authField}: ${message}

What it means

getPluginAuthMap decrypts each stored plugin auth value with decrypt(). When decryption rejects and throwError is true (the default), it throws this error embedding the plugin key, auth field, and the underlying crypto message. With throwError false the failure is logged and that field is simply omitted from the returned map.

Source

Thrown at packages/api/src/agents/auth.ts:65

    /** Single loop through requested pluginKeys */
    for (const pluginKey of pluginKeys) {
      authMap[pluginKey] = {};
      const auths = authsByPlugin.get(pluginKey) || [];

      for (const auth of auths) {
        decryptionPromises.push(
          (async () => {
            try {
              const decryptedValue = await decrypt(auth.value);
              authMap[pluginKey][auth.authField] = decryptedValue;
            } catch (error) {
              const message = error instanceof Error ? error.message : 'Unknown error';
              logger.error(
                `[getPluginAuthMap] Decryption failed for userId ${userId}, plugin ${pluginKey}, field ${auth.authField}: ${message}`,
              );

              if (throwError) {
                throw new Error(
                  `Decryption failed for plugin ${pluginKey}, field ${auth.authField}: ${message}`,
                );
              }
            }
          })(),
        );
      }
    }

    await Promise.all(decryptionPromises);
    return authMap;
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    const plugins = pluginKeys?.join(', ') ?? 'all requested';
    logger.warn(
      `[getPluginAuthMap] Failed to fetch auth values for userId ${userId}, plugins: ${plugins}: ${message}`,
    );
    if (!throwError) {

View on GitHub (pinned to 5ff282f900)

Solutions

  1. If the key rotation is the cause, re-encrypt affected plugin auth values with the new key (or keep the old key available for a decryption pass).
  2. Call getPluginAuthMap with throwError=false where you want best-effort behavior so one bad field does not fail the whole map.
  3. Audit the plugin auth collection for records whose ciphertext is malformed and have users re-enter credentials.
  4. Verify the ENCRYPTION_KEY env var matches the one used when the values were stored.

Example fix

// before
const map = await getPluginAuthMap({ userId, pluginKeys, findPluginAuthsByKeys }); // throws

// after — best-effort, omit undecryptable fields
const map = await getPluginAuthMap({
  userId,
  pluginKeys,
  throwError: false,
  findPluginAuthsByKeys,
});
Defensive patterns

Strategy: try-catch

Validate before calling

// before decrypting, sanity-check ciphertext shape (version prefix, base64 length)
function looksLikeCiphertext(v: unknown): boolean {
  return typeof v === 'string' && v.length > 16 && /^[A-Za-z0-9+/=]+$/.test(v);
}

Try / catch

let map;
try {
  map = await getPluginAuthMap({ userId, pluginKeys, throwError: true, findPluginAuthsByKeys });
} catch (error) {
  if (error instanceof Error && error.message.startsWith('Decryption failed')) {
    logger.warn('plugin auth decrypt failed; continuing with empty map', { error });
    map = pluginKeys.reduce((acc, k) => { acc[k] = {}; return acc; }, {} as PluginAuthMap);
  } else {
    throw error;
  }
}

Prevention

When it happens

Trigger: Calling getPluginAuthMap with throwError=true (default) for a user whose stored IPluginAuth.value cannot be decrypted — typically because the encryption key changed, the ciphertext is corrupt, or the record was encrypted by a different key version.

Common situations: Rotating the app's encryption key without re-encrypting existing plugin auth values; restoring a database backup from a server with a different key; a botched migration that truncated the ciphertext; switching encryption algorithms without a re-encrypt pass.

Related errors


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