Duo Chat sidebar is not able to be disabled entirely
Summary
On GitLab Self-Managed 19.1, the GitLab Duo sidebar panel renders even when GitLab Duo is fully disabled at the instance level via the Application Settings API (duo_features_enabled=false, lock_duo_features_enabled=true, duo_availability=never_on).
This prevents users from disabling GitLab Duo and removing the chat panel using the documented workaround: How to disable GitLab Duo and remove the chat panel entirely on an airgapped/offline/free GitLab instance. That workaround is the only option for instances that cannot disable Duo through the UI (airgapped/offline licenses, or EE instances with no license / GitLab Free).
This is not limited to unlicensed or airgapped instances. Instances with an active subscription are also impacted: when an administrator disables Duo with duo_availability=never_on, non-admin members still see the panel with the "Duo is disabled" empty state, so there is no way to disable the Duo sidebar entirely on any instance.
The settings are applied correctly and still disable Duo functionality (access_duo_entry_point is prevented via the ai_features_banned policy condition), but they no longer suppress the panel shell itself.
Root cause: in 19.1, DuoChatPanel::Component#component_instance (ee/app/components/duo_chat_panel/component.rb) gained branches that render the panel without checking Gitlab::CurrentSettings.duo_never_on?:
show_duo_disabled_non_admin?: rendersDuoDisabledNonAdminComponentfor any Developer-and-below member on a project/group page whereduo_features_enabledis false. This applies to licensed and unlicensed instances alike. The instance-level kill switch itself causes this branch to fire, since the lockedfalsecascades to every container. The gating feature flagduo_disabled_non_admin_empty_state(default_enabled: falsein 19.0) was removed in e84eae3c.can_start_trial?(ee/app/components/duo_chat_panel/start_trial.rb): on self-managed, true for any user with:update_gitlab_subscription(all admins), so admins on unlicensed instances always get theStartTrialComponenttrial promotion panel. Merged into the main panel in edc8bb53.show_subscription_expired?: renders the panel in a blocked state when a non-trial license is present and expired (common for airgapped/legacy licenses), with noduo_never_on?check (introduced in 19.0 via fb1b1ec4).
Only the final access_denied? fallback (!duo_never_on?) respects the setting, which was the branch the documented 18.9-18.11 workaround relied on.
Steps to reproduce
-
Install GitLab EE Self-Managed 19.1.x. Reproducible with no license, an offline/legacy license, or an active subscription.
-
As an instance administrator, apply the documented workaround:
curl --request PUT --header "PRIVATE-TOKEN: <token>" \ --data "duo_features_enabled=false&lock_duo_features_enabled=true&duo_availability=never_on" \ "https://gitlab.example.com/api/v4/application/settings" -
Confirm the API response contains
"duo_features_enabled":false,"lock_duo_features_enabled":true,"duo_availability":"never_on". -
On an unlicensed instance, sign in as an admin and browse any page: the Duo panel renders with the start trial promotion.
-
Sign in as a Developer-level member and visit a project or group page: the Duo panel renders with the "Duo is disabled" empty state. Note this only occurs when the non-admin user is a member of one or more groups. This occurs on both licensed and unlicensed instances.
Example Project
Any project on an affected instance; reproduced on a support lab instance at 19.1.0.
What is the current bug behavior?
The Duo sidebar panel (#duo-chat-panel, including the navigation rail and toggle) renders for all signed-in users despite Duo being disabled and locked instance-wide with duo_availability=never_on:
- Admins on unlicensed instances see the trial promotion empty state (
StartTrialComponent). - Non-admin members of one or more groups see the "Duo is disabled" empty state (
DuoDisabledNonAdminComponent) on project/group pages. This affects licensed instances with an active subscription as well as unlicensed instances. - Instances with an expired legacy license see the subscription-expired blocked state.
There is no application setting, user preference, or feature flag in 19.1 that removes the panel. The Duo sidebar cannot be disabled entirely on any instance.
What is the expected correct behavior?
When duo_availability=never_on (i.e., duo_features_enabled=false + lock_duo_features_enabled=true), the Duo sidebar should not render at all, for any user, matching the documented behavior in 18.9-18.11 and the 19.0 default behavior (where the fallback empty state checked !duo_never_on? and the non-admin empty state feature flag was disabled by default).
never_on is the administrator's explicit statement that Duo must never appear on the instance; trial promotions and "Duo is disabled" states should also respect it.
Relevant logs and/or screenshots
Version analysis:
| Version | Behavior with never_on |
|---|---|
| 18.11 and earlier | Panel fully hidden (documented workaround works) |
| 19.0 | Panel hidden by default (show_empty_state? checks !duo_never_on?; duo_disabled_non_admin_empty_state FF default_enabled: false; no start-trial branch). Exception: expired-license blocked state |
| 19.1 | Panel always renders (start-trial branch added, access-denied merged, non-admin FF removed) |
Output of checks
Not relevant. The behavior is reproducible from code inspection of v19.1.0-ee:
ee/app/components/duo_chat_panel/component.rbee/app/components/duo_chat_panel/start_trial.rbee/app/policies/ee/global_policy.rb(ai_features_bannedprevents chat, but panel rendering does not consult it)
Possible fixes
Add a duo_never_on? guard at the top of DuoChatPanel::Component#render?, so all empty-state branches respect the instance-level kill switch:
def render?
return false unless user
return false if ::Gitlab::CurrentSettings.duo_never_on?
component_instance.present?
endAlternatively, add the check to can_start_trial? and show_duo_disabled_non_admin? individually if the subscription-expired state must remain visible.
Related: the existing feature proposal to allow disabling GitLab Duo in the UI for EE instances with no subscription/offline licenses.
Patch release information for backports
Candidate for backport to 19.1.x under the maintenance policy, since it prevents the documented Support workaround for disabling Duo on airgapped/unlicensed instances and affects licensed instances as well.