HumanSignal/label-studio · error · ValidationError

Project must be an integer.

Error message

Project must be an integer.

What it means

get_hotkey_project in label_studio/users/hotkeys.py validates the project_id query parameter for the hotkeys endpoints: it must be a string of digits matching ^[1-9][0-9]*$ (a positive integer, no leading zeros). Otherwise it raises DRF ValidationError with a field-scoped detail {'project': 'Project must be an integer.'}, producing a 400 response.

Source

Thrown at label_studio/users/hotkeys.py:14

import re

from projects.models import Project
from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError

PROJECT_ID_PATTERN = re.compile(r'^[1-9][0-9]*$')


def get_hotkey_project(user, project_id) -> Project | None:
    if project_id is None:
        return None

    if not isinstance(project_id, str) or PROJECT_ID_PATTERN.fullmatch(project_id) is None:
        raise ValidationError({'project': 'Project must be an integer.'}) from None

    project_id = int(project_id)
    project = Project.objects.filter(
        pk=project_id,
        organization=user.active_organization,
    ).first()
    if project is None:
        raise NotFound('Project not found.')
    if not project.has_permission(user):
        raise PermissionDenied('You do not have access to this project.')
    return project

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Pass the project ID as a plain digit string without leading zeros, e.g. project=123
  2. Convert ints to str before calling get_hotkey_project: get_hotkey_project(user, str(project_id))
  3. Fix the frontend to send the numeric PK of the project, not a name/slug
  4. Strip whitespace and validate with re.fullmatch(r'[1-9][0-9]*', value) client-side

Example fix

// before
get_hotkey_project(user, 42)          # int -> 400
get_hotkey_project(user, "042")       # leading zero -> 400
// after
get_hotkey_project(user, "42")
Defensive patterns

Strategy: validation

Validate before calling

import re
PROJECT_ID = re.compile(r'^[1-9][0-9]*$')
assert isinstance(project_id, str) and PROJECT_ID.fullmatch(project_id), f'bad project id: {project_id!r}'

Type guard

def is_valid_project_id(v):
    return isinstance(v, str) and re.fullmatch(r'[1-9][0-9]*', v) is not None

Try / catch

try:
    project = get_hotkey_project(user, project_id)
except ValidationError as e:
    if 'Project must be an integer.' in str(e.detail):
        project = get_hotkey_project(user, str(int(project_id)))
    else:
        raise

Prevention

When it happens

Trigger: GET/PATCH /api/current-user/reset-token or hotkey routes with ?project=abc, ?project=1.5, ?project=0, ?project=01, ?project=%201 (whitespace), or passing an int-typed value in code so isinstance(project_id, str) fails — the function requires a string even for valid numbers.

Common situations: Frontend passing an unvalidated user-typed project ID; clients passing a Python int instead of str when calling the helper directly; URLs built from route params that can be '0' or non-numeric slugs; double-encoded or whitespace-padded query values.

Related errors


AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29). Data as JSON: /api/errors/ce770977f4485ede. Report an issue: GitHub.