---
title: Troubleshooting | LlamaCloud Admin API
description: 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.

## 404 is the ordinary authorization failure

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](/cookbook/provision-a-team#confirm-your-key-is-user-scoped/index.md).
2. **Is the key from the right region?** See [Wrong-region keys](#a-wrong-region-key-looks-like-a-bad-key).
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](#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.

## 429 means one of four things

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 with                                         | Raised by              | What it is                                                                                                                                                                  |
| ------------------------------------------------------------ | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Rate limit exceeded for user invitations.`                  | `users.add`            | A 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 users`         | `users.add`            | The plan’s seat cap. Pending invites count toward it. Retrying will not help.                                                                                               |
| `You've already reached the maximum number of projects`      | `projects.create`      | The plan’s project cap. Retrying will not help.                                                                                                                             |
| `You've already created the maximum number of organizations` | `organizations.create` | The 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.

## 409 has exactly two causes

- **`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.

## Calls that succeed but do the wrong thing

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

### 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.

- [Python](#tab-panel-26)
- [TypeScript](#tab-panel-27)

```
# 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
)
```

```
// Wrong: 200, and the grant lands on the default project.
await client.organizations.users.addToProject(userId, { organization_id: ORG_ID });


// Right:
await client.organizations.users.addToProject(userId, {
  organization_id: ORG_ID,
  project_id: PROJECT_ID,
});
```

### 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.

- [Python](#tab-panel-28)
- [TypeScript](#tab-panel-29)

```
# 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}],
    )
```

```
// Wrong: both people get both projects.
await 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 (const [email, projectId] of [
  ["a@example.com", PROJECT_A],
  ["b@example.com", PROJECT_B],
]) {
  await client.organizations.users.add(ORG_ID, {
    body: [{ email, project_ids: [projectId], role_id: VIEWER_V2 }],
  });
}
```

### 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.

- [Python](#tab-panel-30)
- [TypeScript](#tab-panel-31)

```
# 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
)
```

```
// Check what they actually hold before and after — a second assignRole adds a row.
const roster = await client.organizations.users.listMembers(ORG_ID);
const member = roster.find((m) => m.user_id === userId);
console.log(member?.roles.map((r) => [r.role.name, r.project_ids]));


// To take a project grant away, remove it explicitly:
await client.organizations.users.removeFromProject(PROJECT_ID, {
  organization_id: ORG_ID,
  user_id: userId,
});
```

### 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.

### One more, not silent but easy to miss

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.

## The roles array is not an admin check

`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.

- [Python](#tab-panel-32)
- [TypeScript](#tab-panel-33)

```
# 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)
```

```
// Wrong — a project admin passes this.
const isAdmin = member.roles.some((r) => r.role.name === "Admin");


// Right.
const isOrgAdmin = member.roles.some(
  (r) => r.role.name === "Admin" && !r.project_ids?.length,
);
```

## A wrong-region key looks like a bad key

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:

| Region        | API base URL                                           |
| ------------- | ------------------------------------------------------ |
| North America | `https://api.cloud.llamaindex.ai` (the client default) |
| Europe        | `https://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`.

## Reading an error

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

- [Python](#tab-panel-34)
- [TypeScript](#tab-panel-35)

```
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.

```
import { APIError } from "@llamaindex/llama-cloud-admin";


try {
  await client.projects.create({ organization_id: ORG_ID, name: "demo" });
} catch (err) {
  if (!(err instanceof APIError)) throw err;
  // There is no err.body in TypeScript. `err.error` is the parsed JSON body;
  // cast it to read the API's `detail` field.
  const detail = (err.error as { detail?: string } | undefined)?.detail;
  const requestId = err.headers?.get("x-request-id");
  console.log(err.status, detail ?? err.message, requestId);
}
```

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 `APIError`. Transport failures raise `APIConnectionError` / `APIConnectionTimeoutError`, which carry no status and no body.

Always capture x-request-id

It is on the response headers of every call, success or failure, and it is the single most useful thing to hand support. Log it alongside the status rather than only the message.

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

## Other status codes

| Status  | On this surface it means                                                                                                                                                                                                                                                                                                                                          |
| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **400** | `organization_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.                                                                                                                                                                         |
| **401** | Expired or revoked key; a key from the wrong region; a project-scoped key naming a different project.                                                                                                                                                                                                                                                             |
| **403** | A 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. |
| **404** | The ordinary authorization denial. See the top of this page.                                                                                                                                                                                                                                                                                                      |
| **409** | Duplicate project name; deleting the organization’s last project.                                                                                                                                                                                                                                                                                                 |
| **422** | Body 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.                                                                                                                                                                                                |
| **429** | One of four limits. Read `detail`.                                                                                                                                                                                                                                                                                                                                |

## Permission table

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

| Operation                                                       | Route                                                                                  | Requires                                                                                                                                                                                               | Denial                                                                                                                                                                                                                                                                                                                                                    |
| --------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `organizations.list`                                            | `GET /api/v2/organizations`                                                            | A 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.get`                                             | `GET /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` / `getUsage`                          | `GET /api/v1/organizations/{organization_id}/usage`                                    | Read 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.list`                                      | `GET /api/v1/organizations/{organization_id}/roles`                                    | A 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` / `listMembers`              | `GET /api/v1/organizations/{organization_id}/users`                                    | Read 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` / `listProjects`            | `GET /api/v1/organizations/{organization_id}/users/{user_id}/projects`                 | Read 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.list`                                                 | `GET /api/v2/projects`                                                                 | A 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.get`                                                  | `GET /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.create`                                               | `POST /api/v2/projects`                                                                | Write 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.update`                                               | `PUT /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.delete`                                               | `DELETE /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.add`                                       | `PUT /api/v1/organizations/{organization_id}/users`                                    | Write 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` / `assignRole`                | `PUT /api/v1/organizations/{organization_id}/users/roles`                              | Write 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` / `addToProject`           | `PUT /api/v1/organizations/{organization_id}/users/{user_id}/projects`                 | Write 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` / `removeFromProject` | `DELETE /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.delete`                                    | `DELETE /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` / `listMine`                                | `GET /api/v2/invites`                                                                  | A valid key. Scoped to your own address.                                                                                                                                                               | None. Lists organization-scope invites only, so someone added with `project_ids` sees nothing here.                                                                                                                                                                                                                                                       |
| `invites.accept`                                                | `POST /api/v2/invites/{invite_id}/accept`                                              | 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>`.                                                                                                                                                                                                                                                         |
| `invites.decline`                                               | `DELETE /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.create`                            | `POST /api/v1/beta/api-keys`                                                           | Read 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.list`                                | `GET /api/v1/beta/api-keys`                                                            | Read 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.delete`                            | `DELETE /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.                                                                                                                     |

### The rare 403 instead of a 404

`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.
