HumanSignal/label-studio · error · NotFound
Project not found.
Error message
Project not found.
What it means
get_hotkey_project raises DRF NotFound after the ID format check when no Project matches both the given pk and the user's active organization. The lookup is scoped to the active organization, so a valid project ID from a different organization also yields 404 'Project not found.'
Source
Thrown at label_studio/users/hotkeys.py:22
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
- Verify the project ID exists and belongs to the user's currently active organization
- Switch the user's active organization to the one owning the project (PATCH /api/current-user with the org id or the org-switch endpoint)
- Refresh the frontend's project list and use a current project ID instead of a cached one
- Check the project wasn't deleted; list projects via GET /api/projects/ under the active org to find a valid ID
Example fix
// before GET /api/hotkeys?project=999 # project lives in another org -> 404 // after # switch to the org that owns the project, then GET /api/hotkeys?project=999
Defensive patterns
Strategy: try-catch
Validate before calling
existing = [p.id for p in client.projects.list()]
if project_id not in existing:
raise ValueError(f'project {project_id} not visible in active organization') Type guard
def project_in_active_org(project, user):
return project is not None and project.organization_id == user.active_organization_id Try / catch
try:
project = get_hotkey_project(user, project_id)
except NotFound:
logger.warning('Project %s not found in active org; refreshing project list', project_id)
projects = client.projects.list()
project = next((p for p in projects if p.id == project_id), None)
if project is None:
raise Prevention
- Re-fetch project IDs after switching active organization
- Handle 404 on hotkey/project routes by refreshing the project list
- Check the project still exists before deep-linking to it
- Remember lookups are scoped to the active organization — a valid ID elsewhere still 404s
When it happens
Trigger: GET/PATCH hotkey endpoints with ?project=<id> where the ID doesn't exist, was deleted, or belongs to another organization than the user's active_organization (multi-org users switching orgs while caching old IDs).
Common situations: Stale project IDs in frontend state after a project was deleted; users in multiple organizations whose active org changed (via UI switch or /api/current-user org switch) so previously valid IDs are invisible; copied URLs/bookmarks referencing projects from another org; typos in the project ID.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Directory indexes are not allowed here.
- “%(path)s” does not exist
- {export_type} format is not converted yet
- Annotation must have "result" fields
- Prediction must have "result" fields
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/b520ee3ab13e79c9.
Report an issue: GitHub.