Comfy-Org/ComfyUI · error · PermissionError
FORBIDDEN
FORBIDDEN
Error message
not owner
What it means
PermissionError raised by the ownership-verified reference fetch when the reference row has a non-empty owner_id that does not match the caller-supplied owner_id. Empty owner_id on the row is treated as unowned and allowed, so the guard only rejects genuine mismatches between two different owners.
Source
Thrown at app/assets/database/queries/asset_reference.py:99
return session.get(AssetReference, reference_id)
def get_reference_with_owner_check(
session: Session,
reference_id: str,
owner_id: str,
) -> AssetReference:
"""Fetch a reference and verify ownership.
Raises:
ValueError: if reference not found or soft-deleted
PermissionError: if owner_id doesn't match
"""
ref = get_reference_by_id(session, reference_id=reference_id)
if not ref or ref.deleted_at is not None:
raise ValueError(f"AssetReference {reference_id} not found")
if ref.owner_id and ref.owner_id != owner_id:
raise PermissionError("not owner")
return ref
def get_reference_by_file_path(
session: Session,
file_path: str,
) -> AssetReference | None:
"""Get a reference by its file path."""
return (
session.execute(
select(AssetReference).where(AssetReference.file_path == file_path).limit(1)
)
.scalars()
.first()
)
def count_active_siblings(View on GitHub (pinned to 1c6d8d45b3)
Solutions
- Pass the owner_id that was recorded when the reference was created (the creating user's ID).
- If the reference is intentionally shared, clear/empty its owner_id in the database so the ownership check is skipped.
- If you administer the system, look up ref.owner_id directly and confirm which account owns the asset.
- Catch PermissionError at the API layer and map it to 403 rather than letting it surface as a 500.
Example fix
// before
ref = get_reference_and_verify_owner(session, reference_id=rid, owner_id=current_uid)
// after
try:
ref = get_reference_and_verify_owner(session, reference_id=rid, owner_id=current_uid)
except PermissionError:
raise HTTPException(status_code=403, detail="not owner") Defensive patterns
Strategy: try-catch
Validate before calling
ref = get_reference_by_id(session, reference_id=rid)
if ref is not None and ref.owner_id and ref.owner_id != caller_owner_id:
# reject before calling the verified fetch
raise HTTPException(status_code=403, detail="not owner") Try / catch
try:
ref = get_reference_and_verify_owner(session, reference_id=rid, owner_id=uid)
except PermissionError:
raise HTTPException(status_code=403, detail="not owner")
except ValueError:
raise HTTPException(status_code=404, detail="reference not found") Prevention
- Propagate the authenticated user's ID as owner_id on every owner-scoped call; never default it ad hoc.
- Remember rows with empty owner_id are unowned and always pass the check.
- Separate the 403 (PermissionError) path from the 404 (ValueError) path in handlers.
When it happens
Trigger: Passing an owner_id different from the reference's ref.owner_id when ref.owner_id is set (non-empty string). Happens on any owner-scoped read/update/delete of another user's reference.
Common situations: Multi-user deployments where references are created by different accounts; a client sending an empty, default, or stale owner/user identifier; tests that create references without an owner and later fetch them with an owner_id set (this passes only because owner_id on the row is empty); mixing up authenticated user IDs between services.
Related errors
AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14).
Data as JSON: /api/errors/cbdccddb7d2a67d8.
Report an issue: GitHub.