BerriAI/litellm · error · ValueError
Unable to record skill ownership: caller has no identity sco
Error message
Unable to record skill ownership: caller has no identity scope.
What it means
When creating a skill, the handler derives an owner from get_primary_resource_owner_scope(user_api_key_dict) (user/team/org/api-key identity) falling back to the explicit user_id. If both are absent — an identity-less caller — it raises ValueError rather than storing a placeholder, because a shared fake owner would let anonymous callers read each other's skills. This is a deliberate multi-tenant isolation guard.
Source
Thrown at litellm/llms/litellm_proxy/skills/handler.py:77
return prisma_client
@staticmethod
async def create_skill(
data: NewSkillRequest,
user_id: str | None = None,
user_api_key_dict: UserAPIKeyAuth | None = None,
) -> LiteLLM_SkillsTable:
prisma_client: Final = await LiteLLMSkillsHandler._get_prisma_client()
skill_id: Final = f"{LITELLM_SKILL_ID_PREFIX}{uuid.uuid4()}"
owner: Final = get_primary_resource_owner_scope(user_api_key_dict) or user_id
if owner is None:
# Identity-less callers (no user_id / team_id / org_id /
# api_key / token) can't be uniquely stamped on the row.
# Stamping a placeholder would let any two such callers see
# each other's skills via the shared owner. ValueError keeps
# this module FastAPI-free per the project layering rule.
raise ValueError("Unable to record skill ownership: caller has no identity scope.")
skill_data: Final[dict[str, Any]] = {
"skill_id": skill_id,
"display_title": data.display_title,
"description": data.description,
"instructions": data.instructions,
"source": "custom",
"created_by": owner,
"updated_by": owner,
}
if data.metadata is not None:
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
skill_data["metadata"] = safe_dumps(data.metadata)
if data.file_content is not None:
from prisma.fields import Base64View on GitHub (pinned to 6c2dcb801b)
Solutions
- Authenticate the request with a valid LiteLLM proxy virtual key (Authorization: Bearer sk-...) so the caller has an identity scope
- Ensure the key/user is bound to a user, team, or organization in the proxy so owner resolution succeeds
- For programmatic calls to the handler, pass user_id explicitly when no UserAPIKeyAuth scope exists
Example fix
# before
resp = client.post("/v1/skills", json=skill_payload) # no auth header
# after
resp = client.post(
"/v1/skills",
headers={"Authorization": "Bearer sk-my-proxy-key"},
json=skill_payload,
) Defensive patterns
Strategy: validation
Validate before calling
def caller_has_identity(user_api_key_dict, user_id: str | None) -> bool:
scope = None
if user_api_key_dict is not None:
scope = getattr(user_api_key_dict, "user_id", None) or getattr(user_api_key_dict, "team_id", None) \
or getattr(user_api_key_dict, "organization_id", None) or getattr(user_api_key_dict, "api_key", None)
return bool(scope or user_id) Try / catch
try:
skill = await LiteLLMSkillsHandler.create_skill(data=payload, user_api_key_dict=auth, user_id=uid)
except ValueError as e:
if "no identity scope" in str(e):
raise HTTPException(status_code=401, detail="Authenticated key required to create skills") from e Prevention
- Always send a valid proxy virtual key on skills routes; anonymous access is intentionally rejected
- Bind keys to a user/team/org in the proxy so owner attribution always resolves
- In tests, construct UserAPIKeyAuth fixtures with at least one identity field instead of empty objects
When it happens
Trigger: POSTing to the skills endpoint without an authenticated identity: missing/invalid API key header so user_api_key_dict carries no scopes, and no user_id supplied; internal/test invocations of create_skill passing neither argument.
Common situations: Calling the skills route with a personal (no team/org binding) key that failed auth parsing; scripts hitting the proxy without the Authorization header; auth middleware disabled in dev so UserAPIKeyAuth is empty.
Related errors
- Prisma client is not initialized. Database connection requir
- Error: {response.status_code} - {response.text}
- Missing Authorization header
- Invalid bearer token
- Invalid API key
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/81c4606f7912dbb3.
Report an issue: GitHub.