Unable to add Topic that results in the same slug as an existing topic

Summary

Unable to add an additional topic when the same slug is generated

Steps to reproduce

  1. Create a new project
  2. Add a topic like bx::code::c (this can be done via the UI or API).
  3. Use the PUT projects/:id REST API to add a topic such as bx::code::c++ to the project. They key here is to use something that will generate the same slug (e.g. bxcodec)

Example Project

Not Applicable

What is the current bug behavior?

The topic is not added and the project is not updated. If using the WebUI you get a generic error. If using the REST API you get a 200 OK with an empty response.

The concern with the 200 OK has been logged in #548609

What is the expected correct behavior?

GitLab detects the condition,generates a unique slug, adds the Topic and Project::Topic

Additional Troubleshooting

If you change the slug on the existing Topic e.g. bxcodec to bxcodex the new topic can be added.

Relevant logs and/or screenshots

There are no logs that are generated when this problem occurs, which is a problem on its own. However, with DEBUG logging enabled after it queries the database for the slug it stops.

gitlab-rails/production.log
Projects::ProjectTopic Delete All (0.9ms)  /*application:web,correlation_id:01JXWYXY62FGSTMNM1111CANG0,endpoint_id:PUT /api/:version/projects/:id,db_config_database:gitlabhq_production,db_config_name:main*/ DELETE FROM "project_topics" WHERE ("project_topics"."id") IN (SELECT "project_topics"."id" FROM "project_topics" WHERE "project_topics"."project_id" = 2 AND "project_topics"."topic_id" = 2 ORDER BY "project_topics"."id" ASC)
Projects::Topic Update All (2.6ms)  /*application:web,correlation_id:01JXWYXY62FGSTMNM1111CANG0,endpoint_id:PUT /api/:version/projects/:id,db_config_database:gitlabhq_production,db_config_name:main*/ UPDATE "topics" SET "total_projects_count" = COALESCE("total_projects_count", 0) - 1 WHERE "topics"."id" = 2
Projects::Topic Load (0.7ms)  /*application:web,correlation_id:01JXWYXY62FGSTMNM1111CANG0,endpoint_id:PUT /api/:version/projects/:id,db_config_database:gitlabhq_production,db_config_name:main*/ SELECT "topics".* FROM "topics" WHERE "topics"."organization_id" = 1 AND (lower(name) = 'bx::code::c++') ORDER BY "topics"."total_projects_count" DESC LIMIT 1
Projects::Topic Exists? (0.7ms)  /*application:web,correlation_id:01JXWYXY62FGSTMNM1111CANG0,endpoint_id:PUT /api/:version/projects/:id,db_config_database:gitlabhq_production,db_config_name:main*/ SELECT 1 AS one FROM "topics" WHERE LOWER("topics"."name") = LOWER('bx::code::c++') AND "topics"."organization_id" = 1 LIMIT 1
Projects::Topic Exists? (0.6ms)  /*application:web,correlation_id:01JXWYXY62FGSTMNM1111CANG0,endpoint_id:PUT /api/:version/projects/:id,db_config_database:gitlabhq_production,db_config_name:main*/ SELECT 1 AS one FROM "topics" WHERE LOWER("topics"."slug") = LOWER('bxcodec') AND "topics"."organization_id" = 1 LIMIT 1
Namespace Load (1.5ms)  /*application:web,correlation_id:01JXWYXY62FGSTMNM1111CANG0,endpoint_id:PUT /api/:version/projects/:id,db_config_database:gitlabhq_production,db_config_name:main*/ SELECT "namespaces"."id", "namespaces"."name", "namespaces"."path", "namespaces"."owner_id", "namespaces"."created_at", "namespaces"."updated_at", "namespaces"."type", "namespaces"."description", "namespaces"."avatar", "namespaces"."membership_lock", "namespaces"."share_with_group_lock", "namespaces"."visibility_level", "namespaces"."request_access_enabled", "namespaces"."ldap_sync_status", "namespaces"."ldap_sync_error", "namespaces"."ldap_sync_last_update_at", "namespaces"."ldap_sync_last_successful_update_at", "namespaces"."ldap_sync_last_sync_at", "namespaces"."description_html", "namespaces"."lfs_enabled", "namespaces"."parent_id", "namespaces"."shared_runners_minutes_limit", "namespaces"."repository_size_limit", "namespaces"."require_two_factor_authentication", "namespaces"."two_factor_grace_period", "namespaces"."cached_markdown_version", "namespaces"."project_creation_level", "namespaces"."runners_token", "namespaces"."file_template_project_id", "namespaces"."saml_discovery_token", "namespaces"."runners_token_encrypted", "namespaces"."custom_project_templates_group_id", "namespaces"."auto_devops_enabled", "namespaces"."extra_shared_runners_minutes_limit", "namespaces"."last_ci_minutes_notification_at", "namespaces"."last_ci_minutes_usage_notification_level", "namespaces"."subgroup_creation_level", "namespaces"."max_pages_size", "namespaces"."max_artifacts_size", "namespaces"."mentions_disabled", "namespaces"."default_branch_protection", "namespaces"."max_personal_access_token_lifetime", "namespaces"."push_rule_id", "namespaces"."shared_runners_enabled", "namespaces"."allow_descendants_override_disabled_shared_runners", "namespaces"."traversal_ids", "namespaces"."organization_id" FROM "namespaces" WHERE "namespaces"."owner_id" = 2 AND "namespaces"."type" = 'User' LIMIT 1
User Update (3.3ms)  /*application:web,correlation_id:01JXWYXY62FGSTMNM1111CANG0,endpoint_id:PUT /api/:version/projects/:id,db_config_database:gitlabhq_production,db_config_name:main*/ UPDATE "users" SET "updated_at" = '2025-06-16 18:09:55.054527', "last_activity_on" = '2025-06-16' WHERE "users"."id" = 2

Output of checks

This bug happens on GitLab.com

Results of GitLab environment info

Expand for output related to GitLab environment info
System information
System:         Ubuntu 20.04
Proxy:          no
Current User:   git
Using RVM:      no
Ruby Version:   3.2.5
Gem Version:    3.6.7
Bundler Version:2.6.5
Rake Version:   13.0.6
Redis Version:  7.2.7
Sidekiq Version:7.3.9
Go Version:     unknown
GitLab information
Version:        18.0.1-ee
Revision:       3426be1b938
Directory:      /opt/gitlab/embedded/service/gitlab-rails
DB Adapter:     PostgreSQL
DB Version:     16.8
URL:            https://gitlab.example.com
HTTP Clone URL: https://gitlab.example.com/some-group/some-project.git
SSH Clone URL:  git@gitlab.example.com:some-group/some-project.git
Elasticsearch:  no
Geo:            no
Using LDAP:     no
Using Omniauth: yes
Omniauth Providers:
GitLab Shell
Version:        14.42.0
Repository storages:


default:      unix:/var/opt/gitlab/gitaly/gitaly.socket
GitLab Shell path:              /opt/gitlab/embedded/service/gitlab-shell

Gitaly


        
      default Address:      unix:/var/opt/gitlab/gitaly/gitaly.socket

        
      default Version:      18.0.1

        
      default Git Version:  2.49.0.gl2

Possible fixes

https://gitlab.com/gitlab-org/gitlab/-/blame/master/app/models/projects/topic.rb?ref_type=heads#L20

/cc @ulisesf

Edited by 🤖 GitLab Bot 🤖