Skip to content

AppDisableListener enqueues invalid delete_provider_id (bare provider id) → freezes the whole action queue #258

Description

@weltmaister

AppDisableListener enqueues invalid delete_provider_id (bare provider id) → blocks the whole action queue

Summary

AppDisableListener schedules a delete_provider_id action using the bare provider id (e.g. mail) instead of the full provider key appId__providerId (e.g. mail__mail). The backend rejects it because a provider id must match ^[a-zA-Z0-9_-]+__[a-zA-Z0-9_-]+$. Since the backend validates the entire action batch at once, this single invalid item makes the backend's action-processing thread throw on every poll, freezing the complete oc_context_chat_action_queue indefinitely (access-declaration updates, deletions, etc. stop propagating).

Affected code

lib/Listener/AppDisableListener.php (present unchanged on main):

foreach ($this->providerConfig->getProviders() as $key => $values) {   // $key = "appId__providerId", e.g. "mail__mail"
    $identifierValues = explode('__', $key, 2);
    ...
    [$appId, $providerId] = $identifierValues;                          // $appId="mail", $providerId="mail"
    if ($appId !== $event->getAppId()) {
        continue;
    }
    $this->providerConfig->removeProvider($appId, $providerId);
    $this->actionService->deleteProvider($providerId);                 // BUG: passes bare "mail"
}

ActionScheduler::deleteProvider() stores the argument verbatim as the payload:

public function deleteProvider(string $providerKey): void {
    $payload = json_encode(['providerId' => $providerKey]);   // -> {"providerId":"mail"}
    ...
}

Every other caller passes the full key. Compare the public API path, ContentManager::deleteProvider():

$this->actionService->deleteProvider(ProviderConfigService::getConfigKey($appId, $providerId)); // "mail__mail"

Root cause

AppDisableListener already holds the correct full key in $key, but passes only the split-off $providerId to ActionScheduler::deleteProvider(). The backend's provider-id validator (context_chat_backend/context_chat_backend/types.py, is_valid_provider_id) then rejects mail:

pydantic_core._pydantic_core.ValidationError: 1 validation error for ActionsQueueItems
actions.<id>.delete_provider_id.payload.providerId
  Value error, providerId must be a valid provider ID string
    input_value='mail'

Impact

Because context_chat_backend's updates_processing_thread validates the whole ActionsQueueItems batch in one model_validate() call, one invalid item raises and no actions in the batch are processed. The action queue stalls completely and never recovers on its own; a stale row keeps poisoning every poll. Observed effect: 500+ update_access_decl_source_id actions stuck for ~1 day; ACL/share changes not reflected in semantic search results until the poison rows were removed manually.

Trigger

AppDisableEvent — fired whenever an app that registered a context_chat content provider is disabled, including during app updates / occ upgrade. The Nextcloud Mail app registers a content provider with id mail, so each disable produces one poisoned delete_provider_id: mail row.

Suggested fix (context_chat)

Pass the full provider key:

-            $this->providerConfig->removeProvider($appId, $providerId);
-            $this->actionService->deleteProvider($providerId);
+            $this->providerConfig->removeProvider($appId, $providerId);
+            $this->actionService->deleteProvider($key);

($key is already appId__providerId; equivalently ProviderConfigService::getConfigKey($appId, $providerId).)

Secondary hardening (context_chat_backend)

updates_processing_thread should validate/skip actions per item (as the files path already does via ItemValidationError) rather than aborting the entire batch, so a single malformed action can never freeze the whole queue.

Environment

  • context_chat (PHP app): 5.4.0
  • context_chat_backend: 5.4.1
  • Nextcloud: AIO
  • Bug present on nextcloud/context_chat@main (lib/Listener/AppDisableListener.php, line ~51), unchanged; no existing issue/PR found.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions