Skip to content
Product Docs

Troubleshooting

What each status code actually means on the admin surface, the four different 429s, the calls that return 200 while doing something else, and the full permission table.

The status codes on this surface do not always mean what they mean elsewhere. This page decodes them, then lists every operation with the access it needs and the exact failure you get without it.

Nearly every organization-scoped call denies with 404, not 403:

404 Organization <organization_id> not found

The organization exists. You are not an admin of it. The API masks existence deliberately, so that probing ids cannot be used to enumerate organizations — which means a 404 is ambiguous by design between “wrong id” and “no access”.

If you are certain the id is right, you lack write access to the organization. Check, in order:

  1. Is the key user-scoped? A project-scoped key cannot reach the organization surface at all. The read-only probe is in the recipe.
  2. Is the key from the right region? See Wrong-region keys.
  3. Are you an organization admin — not a project admin? Reading Admin off a member’s roles array is not the check you think it is; see The roles array is not an admin check.

Three exceptions to the 404 rule:

  • organizations.list has no denial at all. It is membership-scoped. An organization you have no grant in is simply absent from the page — never 403, never 404. An empty page and a permissions problem look identical.
  • Read access is enough for the read calls. organizations.get, organizations.get_usage, users.list_members, and users.list_projects need only read access to the organization, or any project grant inside it. A viewer can read the organization and still 404 on every write.
  • Project routes are different. projects.get denies with 404 Project <id> is not found. projects.update and projects.delete return 404 if you cannot read the project and 403 if you can read it but not write it — and they carry no organization check at all, so a project admin who is not an organization admin can delete a project while 404ing on everything else.

All four are plain 429s. Only detail distinguishes them, and only one of them is a rate limit you should back off from.

detail starts withRaised byWhat it is
Rate limit exceeded for user invitations.users.addA real rate limit: 10 requests per 60 seconds per organization. It counts requests, not members. Wait and retry.
You've already reached the maximum number of usersusers.addThe plan’s seat cap. Pending invites count toward it. Retrying will not help.
You've already reached the maximum number of projectsprojects.createThe plan’s project cap. Retrying will not help.
You've already created the maximum number of organizationsorganizations.createThe per-account organization cap. The same gate returns 403 User does not have access to create organizations. when your account may not create organizations at all.

Read the live numbers behind the two plan caps with organizations.get_usage(organization_id) — plan.limits.max_users and plan.limits.max_projects (null means unlimited) against usage.total_users. On a BYOC or self-hosted deployment the plan caps do not apply at all; only the invite rate limit remains.

  • projects.create and projects.update — A project with name X already exists in organization Y. Project names are unique within an organization, and a deleted project still holds its name. So the conflicting project may not appear in projects.list; if a lookup after the 409 comes back empty, that is why. Pick a different name.
  • projects.delete — An organization must keep at least one project. The organization’s last project cannot be deleted.

No other operation on this surface returns 409.

These return 2xx. Nothing in the response tells you what happened.

add_to_project without project_id targets the default project

Section titled “add_to_project without project_id targets the default project”

project_id is optional in the signature. Omit it and the call grants access to the organization’s default project — creating a default project if the organization has none — and returns 200. The response does not name the project it touched.

# Wrong: 200, and the grant lands on the default project.
client.organizations.users.add_to_project(user_id, organization_id=ORG_ID)
# Right:
client.organizations.users.add_to_project(
user_id, organization_id=ORG_ID, project_id=PROJECT_ID
)

A multi-member users.add batch cross-assigns project ids

Section titled “A multi-member users.add batch cross-assigns project ids”

Every entry’s project_ids is applied to every member in the batch, so each person ends up with the union of everyone’s project grants. Send one member per call.

# Wrong: both people get both projects.
client.organizations.users.add(
ORG_ID,
body=[
{"email": "a@example.com", "project_ids": [PROJECT_A], "role_id": VIEWER_V2},
{"email": "b@example.com", "project_ids": [PROJECT_B], "role_id": VIEWER_V2},
],
)
# Right: one call each.
for email, project_id in [("a@example.com", PROJECT_A), ("b@example.com", PROJECT_B)]:
client.organizations.users.add(
ORG_ID,
body=[{"email": email, "project_ids": [project_id], "role_id": VIEWER_V2}],
)

assign_role adds a role, it never replaces one

Section titled “assign_role adds a role, it never replaces one”

Grants are additive. Assigning a second role leaves the first one live, and the more permissive of the two wins. There is no downgrade and no revoke through assign_role: after granting Admin on a project and then ViewerV2 across the organization, the member holds both.

The only calls that remove access are users.remove_from_project, which drops one project grant, and users.delete, which removes the person from the organization.

# Check what they actually hold before and after — a second assign_role adds a row.
member = next(m for m in client.organizations.users.list_members(ORG_ID)
if m.user_id == user_id)
print([(r.role.name, r.project_ids) for r in member.roles])
# To take a project grant away, remove it explicitly:
client.organizations.users.remove_from_project(
PROJECT_ID, organization_id=ORG_ID, user_id=user_id
)

Re-adding an existing member re-sends the email and clears a flag

Section titled “Re-adding an existing member re-sends the email and clears a flag”

users.add against someone who is already a member re-applies the membership from your request. Your request does not carry their default-organization setting, so that setting is cleared — and the invitation email is sent again. List the members and skip the ones already present rather than adding idempotently.

At organization scope, assign_role collapses anything that is not Admin or ViewerV2 down to a plain viewer grant. Granting AgentViewer organization-wide does not produce an AgentViewer organization grant, and nothing in the response flags the substitution — read role.name back from list_members if it matters.

users.list_members returns each member’s roles, and that array mixes organization-scope and project-scope grants. A project-level admin grant is labelled Admin exactly like an organization-wide one. Checking the name alone reports someone as an organization admin who will 404 on every organization write — and you can manufacture that false positive yourself by granting a colleague Admin on a single project.

The discriminator is project_ids. It is populated for a project grant and empty only for an organization-wide one.

# Wrong — a project admin passes this.
is_admin = any(r.role.name == "Admin" for r in member.roles)
# Right.
is_admin = any(r.role.name == "Admin" and not r.project_ids for r in member.roles)

A key issued in one region is rejected by the other with:

401 Invalid API Key. Please check your region https://developers.llamaindex.ai/python/cloud/general/regions.

It is indistinguishable from an expired or mistyped key, and regenerating the key does not help. Check the base URL first:

RegionAPI base URL
North Americahttps://api.cloud.llamaindex.ai (the client default)
Europehttps://api.cloud.eu.llamaindex.ai

Both clients read LLAMA_CLOUD_ADMIN_BASE_URL for the override.

401 has two other causes on this surface: an expired or revoked key, and a project-scoped key naming a different project — Project ID specified in the URL does not match the project ID associated with the API key.

The two clients expose the response body differently. Use each one’s own idiom; porting the other’s will silently read undefined.

from llama_cloud_admin import APIStatusError
try:
client.projects.create(organization_id=ORG_ID, name="demo")
except APIStatusError as err:
# There is no err.error in Python. The body is typed `object | None`,
# so guard it before subscripting.
detail = err.body.get("detail") if isinstance(err.body, dict) else None
request_id = err.response.headers.get("x-request-id")
print(err.status_code, detail or err.message, request_id)

Exception classes, all exported from the package root: BadRequestError (400), AuthenticationError (401), PermissionDeniedError (403), NotFoundError (404), ConflictError (409), UnprocessableEntityError (422), RateLimitError (429), InternalServerError (5xx). They all subclass APIStatusError, which subclasses APIError. Transport failures raise APIConnectionError / APITimeoutError, which carry no status and no body.

Both clients retry 5xx responses and connection failures twice by default before raising, so an InternalServerError that reaches you has already been retried.

StatusOn this surface it means
400organization_id is required when not using a project-scoped API key on projects.list (this is the key-scope probe); User cannot assign role to themselves; an unrecognized role id.
401Expired or revoked key; a key from the wrong region; a project-scoped key naming a different project.
403A project-scoped key on projects.create; projects.update / projects.delete when you can read the project but not write it; invites.accept on an invite addressed to someone else; api_keys.delete on someone else’s user-scoped key; api_keys.create against a disabled project; the rare assign_role / users.add case described below the table.
404The ordinary authorization denial. See the top of this page.
409Duplicate project name; deleting the organization’s last project.
422Body shape: project_ids omitted from a users.add entry, an entry with neither email nor user_id, or organization_id missing from the assign_role body.
429One of four limits. Read detail.

Every operation on the organization-administration tier, the access it needs, and what you get without it.

OperationRouteRequiresDenial
organizations.listGET /api/v2/organizationsA valid key. Results are limited to organizations you belong to.None. An organization you have no grant in is absent from the page — never 403, never 404. The name filter is a case-insensitive partial match.
organizations.getGET /api/v2/organizations/{organization_id}Read access to the organization, or any project grant inside it.404 Organization <id> not found. Read access is enough here; the writes below need more.
organizations.get_usage / getUsageGET /api/v1/organizations/{organization_id}/usageRead access to the organization, or any project grant inside it.404 Organization <id> not found. Read-only and safe to call first — this is where the plan caps come from.
organizations.roles.listGET /api/v1/organizations/{organization_id}/rolesA valid key. The organization is not checked.None. Succeeding here is not evidence that you administer that organization; the id only filters which roles are visible.
organizations.users.list_members / listMembersGET /api/v1/organizations/{organization_id}/usersRead access to the organization, or any project grant inside it.404 Organization <id> not found. Each member’s roles mixes organization-scope and project-scope grants.
organizations.users.list_projects / listProjectsGET /api/v1/organizations/{organization_id}/users/{user_id}/projectsRead access to the organization, or any project grant inside it.404 Organization <id> not found. Returns an empty list rather than an error for an empty user id.
projects.listGET /api/v2/projectsA valid key. organization_id is required unless the key is project-scoped.400 organization_id is required when not using a project-scoped API key — the key-scope probe. A project-scoped key gets 200 and its one project. The name filter is an exact match.
projects.getGET /api/v2/projects/{project_id}Read access to the project. Organization admins inherit it.404 Project <id> is not found — not 403; the read check runs first. A project-scoped key naming a different project gets 401.
projects.createPOST /api/v2/projectsWrite access to the organization — an organization admin.404 Organization <id> not found. Also 403 API key is restricted to a single project for a project-scoped key; 429 for the plan’s project cap; 409 for a duplicate name.
projects.updatePUT /api/v2/projects/{project_id}Read and write access to the project. No organization check.404 if you cannot read it; 403 if you can read but not write it; 409 on a duplicate name.
projects.deleteDELETE /api/v2/projects/{project_id}Read and write access to the project. No organization check — a project admin who is not an organization admin can delete it.404 if you cannot read it; 403 if you can read but not write it; 409 An organization must keep at least one project.
organizations.users.addPUT /api/v1/organizations/{organization_id}/usersWrite access to the organization; the organization-wide shape additionally requires that you are an organization admin.404 Organization <id> not found is the ordinary denial. Then, in order: 429 invite rate limit; 429 seat cap; 422 body shape; 403 User is not an admin and cannot assign roles and 400 User cannot assign role to themselves, unless the entry pairs non-empty project_ids with Admin/ViewerV2/AgentViewer. 400 for an unrecognized role id.
organizations.users.assign_role / assignRolePUT /api/v1/organizations/{organization_id}/users/rolesWrite access to the organization, plus an organization-wide Admin role.404 Organization <id> not found for anyone without write access — the ordinary denial. 403 User is not an admin and cannot assign roles only in the rare case below. 400 User cannot assign role to themselves. 400 for an unrecognized role id. 422 when organization_id is missing from the body.
organizations.users.add_to_project / addToProjectPUT /api/v1/organizations/{organization_id}/users/{user_id}/projectsWrite access to the organization and read access to the named project.404 Organization <id> not found first, then 404 Project <id> is not found. Silent hazard: omit project_id and it targets the organization’s default project, creating one if absent — 200, wrong project.
organizations.users.remove_from_project / removeFromProjectDELETE /api/v1/organizations/{organization_id}/users/{user_id}/projects/{project_id}Write access to the organization.404 Organization <id> not found. Removing a grant the member did not have is not reported as an error.
organizations.users.deleteDELETE /api/v1/organizations/{organization_id}/users/{member_user_id}Write access to the organization.404 Organization <id> not found; 404 User not removed from Organization. when nothing was removed. The path segment is read as an email if it contains @, otherwise as a user id. There is no last-admin guard and no self-removal guard.
invites.list_mine / listMineGET /api/v2/invitesA valid key. Scoped to your own address.None. Lists organization-scope invites only, so someone added with project_ids sees nothing here.
invites.acceptPOST /api/v2/invites/{invite_id}/acceptA valid key. The invite must be addressed to you.404 Invite <id> not found or already redeemed; 403 Invite <id> is not addressed to user <id>.
invites.declineDELETE /api/v2/invites/{invite_id}A valid key. The invite must be addressed to you.404 Invite <id> not found or already redeemed; 403 Invite <id> is not addressed to user <id>. Declining deletes the pending grants for that address across the organization and its projects, so a later re-invite issues fresh ones.
api_keys.create / apiKeys.createPOST /api/v1/beta/api-keysRead access to the project named in project_id. Omit it and there is nothing to check — unless you present a project-scoped key, which substitutes its own project and is then checked against that.404 Project <id> is not found — this one goes through the same resolver as projects.get, so the wording differs from the row below. A project-scoped key naming a different project gets 401 first, before any permission check.
api_keys.list / apiKeys.listGET /api/v1/beta/api-keysRead access to the project, when you pass project_id. Called bare it returns your own keys and checks nothing.404 Project <id> not found or access denied. Secrets are masked on every row; no read returns one.
api_keys.delete / apiKeys.deleteDELETE /api/v1/beta/api-keys/{api_key_id}Key-management permission on the key’s project — more than read, because project keys are shared. A key with no project only needs to be yours.404 Project <id> not found or access denied without the permission, which is the usual case for a shared project key; 404 for a key id that does not exist; 403 Access denied to this API key for someone else’s user-scoped key.

assign_role, and the organization-wide shape of users.add, are the only operations here that can answer 403 User is not an admin and cannot assign roles. If you administer the organization you will never see it — 404 is the denial you get. Treat a 403 from either call exactly as you would the 404: the key you are holding does not administer the organization you named.

On users.add, that 403 and the self-target 400 User cannot assign role to themselves are both reachable unless the entry pairs a non-empty project_ids with Admin, ViewerV2, or AgentViewer — that combination takes a different path and raises neither. project_ids: null, or project ids paired with Viewer, can still raise both.