Limit the number of labels on a work item to 100

What does this MR do and why?

Caps the number of labels that can be applied to a work item (issues, epics, tasks, and any other type on the issues table) at 100, via a new Issue::MAX_NUMBER_OF_LABELS constant.

The Work Item REST API returns all labels inline and unpaginated, so an unbounded label count is a response-size denial-of-service vector. Production data (gathered in the linked issue) shows this limit is safe to introduce:

  • p50 = 2 labels, p99 = 9, p99.9 = 34
  • Only 46 work items across GitLab.com sit above 100 labels, all in small, inactive projects consistent with automated or test usage

100 gives roughly 3x headroom over p99.9, and matches two existing precedents: the GraphQL labels page max (100) and IssuableLink::MAX_LINKS_COUNT (100).

Enforcement is delta-only — existing over-limit work items stay editable (labels can be removed, just not added) — and applies only to work items; merge requests are unaffected, since production data shows legitimate MR workflows with 300-440 labels.

The check is behind the limit_labels_per_work_item feature flag (gitlab_com_derisk, disabled by default, root namespace as the actor), so it can be rolled out per top-level group on GitLab.com and disabled instantly if a workflow we did not see in the data turns out to depend on more than 100 labels. Rollout issue: #629454. Because the flag is off by default there is no changelog entry yet; it will be added when the flag is enabled by default.

Is this a breaking change?

No. There are no GraphQL schema changes — the cap surfaces through the mutation errors field like any other validation failure (title length, assignee cap), and the REST paths return a 400 with a clear message. Behaviorally this follows the established pattern for new application limits (assignees capped at 200, linked items at 100), which are introduced without a deprecation cycle when they protect instance availability. Measured impact on GitLab.com is negligible: 46 of ~85 million labeled work items sit above 100 labels, all consistent with automated or test usage. Enforcement is delta-only, so no existing data or read path changes — the only rejected operation is adding labels past the cap.

References

Resolves #606182

Related: https://gitlab.com/gitlab-org/gitlab/-/work_items/607734 (import-path label cap — should align on the same limit value)

Screenshots or screen recordings

Not applicable, this is a backend-only change with no UI impact.

How to set up and validate locally

  1. In a Rails console, enable the feature flag:

    Feature.enable(:limit_labels_per_work_item)
  2. Still in the console, verify the cap is enforced and nothing partially persists:

    project = Project.find_by_full_path('<your-project>')
    user = User.find_by(username: 'root')
    issue = Issue.create!(project: project, namespace: project.project_namespace, author: user, title: 'cap test')
    labels = 101.times.map { |i| Labels::FindOrCreateService.new(user, project, title: "cap-#{i}").execute }
    Issues::UpdateService.new(container: project, current_user: user, params: { add_label_ids: labels.map(&:id) }).execute(issue)

    The update raises Issuable::Callbacks::Base::Error, and issue.reload.labels stays empty — nothing gets persisted.

  3. Via the legacy REST API, PUT /projects/:id/issues/:iid with 101 labels in add_labels returns 400 with "Cannot add more than 100 labels to a work item." instead of a 500.

  4. A /label quick action that would push a work item past the cap adds a command-failure message to the note instead of raising an error.

  5. Adding labels to a merge request is unaffected at any count, confirming the Issue-only guard works as intended.

  6. Disable the flag (Feature.disable(:limit_labels_per_work_item)) and repeat the console step above: the update now succeeds and all 101 labels are applied.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Daniyal Arshad

Merge request reports

Loading
Loading