Skip to content

AutoTranslate: switching Service Provider while enabled silently keeps translating with the old provider #41197

Description

@Freedom101

Description

Changing AutoTranslate_ServiceProvider while AutoTranslate_Enabled is true does not rebind the message translation callback. New messages keep being sent to the previously selected provider until Auto-Translate is toggled off and on again (or the server restarts).

The failure is invisible in the admin UI: read paths such as autotranslate.getSupportedLanguages resolve the active provider at call time, so the target-language dropdown correctly reflects the new provider while message translation still goes to the old one.

Steps to reproduce

  1. Set AutoTranslate_Enabled = true with the default provider (google-translate), no API key.
  2. Switch AutoTranslate_ServiceProvider to libre-translate (or any other provider) and configure it correctly.
  3. Send a message in a channel where another member has auto-translate enabled.

Expected behavior

The message is translated by the newly selected provider.

Actual behavior

The message is handed to the previously selected provider. With an unconfigured Google provider the server logs show (minutes after the provider was switched):

{"level":50,...,"msg":"Error translating message","err":{"type":"Error","message":"Forbidden","stack":"Error: Forbidden\n    at GoogleAutoTranslate._translateMessage (app/autotranslate/server/googleTranslate.ts:154:12)..."}}

while at the same time GET /api/v1/autotranslate.getSupportedLanguages correctly requests http://libretranslate:5000/languages.

Toggling AutoTranslate_Enabled off and on immediately fixes it — the next message is translated by the new provider.

Root cause

In apps/meteor/app/autotranslate/server/autotranslate.ts, TranslationProviderRegistry.registerCallbacks() registers the afterSaveMessage callback with the provider instance captured in a closure, under the fixed id autotranslate:

static registerCallbacks(): void {
    if (!TranslationProviderRegistry.enabled) {
        callbacks.remove('afterSaveMessage', 'autotranslate');
        return;
    }

    const provider = TranslationProviderRegistry.getActiveProvider();
    if (!provider) {
        return;
    }

    callbacks.add(
        'afterSaveMessage',
        (message, { room }) => provider.translateMessage(message, { room }),
        callbacks.priority.MEDIUM,
        'autotranslate',
    );
}

setCurrentProvider() calls registerCallbacks() again after a provider change, but Callbacks.add() in apps/meteor/server/lib/callbacks/callbacksBase.ts is a no-op when a callback with the same id is already registered:

const callbacks = this.getCallbacks(hook);

if (callbacks.some((cb) => cb.id === id)) {
    return () => {
        this.remove(hook, id);
    };
}

So the closure bound to the old provider stays registered. setEnable(false) goes through callbacks.remove, which is why toggling the enabled setting works around it.

Suggested fix

Either remove before re-adding in registerCallbacks():

callbacks.remove('afterSaveMessage', 'autotranslate');
callbacks.add('afterSaveMessage', ...);

or resolve the provider at call time instead of capturing it:

callbacks.add(
    'afterSaveMessage',
    (message, { room }) => TranslationProviderRegistry.getActiveProvider()?.translateMessage(message, { room }),
    callbacks.priority.MEDIUM,
    'autotranslate',
);

The second variant also removes the ordering sensitivity between the AutoTranslate_ServiceProvider and AutoTranslate_Enabled settings watchers at startup.

Possibly related

#41100 (DeepL AutoTranslate not working after upgrade to 8.5.1) may share this root cause if the provider or enabled settings were changed at runtime.

Server setup information

  • Version of Rocket.Chat Server: 8.6.0 (bug also present on current develop, apps/meteor/app/autotranslate/server/autotranslate.ts @ 2f91e02)
  • License Type: Community
  • Number of Running Instances: 1 (monolith)
  • DB Replicaset Oplog: enabled
  • NodeJS Version: bundled (official docker image registry.rocket.chat/rocketchat/rocket.chat:8.6.0)
  • MongoDB Version: 8.2

Additional context

Reproduced with Google → LibreTranslate, but the mechanism is provider-agnostic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions