Error Reference
Complete reference of HTTP status codes, validation errors, sync errors, and task error codes returned by the Bridge API.
This page is a consolidated reference for every error the Bridge API can return. Bookmark it -- when your connector hits an unexpected response, start here.
HTTP Status Codes
The Bridge API uses standard HTTP status codes. Every endpoint returns one of the following:
| Status | Meaning | When returned |
|---|---|---|
200 OK | Request succeeded | Sync page push, task list, task detail, sync progress, resource type update |
201 Created | Resource created | App created, resource type registered, sync session started |
202 Accepted | Request accepted for async processing | Sync session completed (cleanup runs in the background) |
204 No Content | Request succeeded with no response body | Sync session abandoned |
400 Bad Request | Malformed request | Invalid JSON, schema validation failures (missing required fields, invalid field types), attempting to push to a completed/abandoned sync |
401 Unauthorized | Authentication failed | Missing Authorization header, invalid API key, expired key |
403 Forbidden | Insufficient permissions | API key does not have access to the requested app |
404 Not Found | Resource does not exist | App, resource type, sync session, or task not found. Also returned when clone_from references a nonexistent app |
409 Conflict | State conflict | Duplicate resource type slug, or task already in a terminal state (completed/failed) |
422 Unprocessable Entity | Business-rule validation failed | Unknown membership/assignment slugs or database integrity errors when pushing a sync page |
500 Internal Server Error | Unexpected server error | Unhandled exception during sync processing |
Error Response Format
Most error responses return a JSON body with a detail field:
{
"detail": "Human-readable error message."
}Both 400 and 422 responses from sync page pushes use the detail field format -- see Sync Validation Errors below.
Sync Validation Errors
When you push a page of records (PUT .../sync/{sync_id}/{resource_type_slug}/), the API validates every record before processing. If any record fails validation, the entire page is rejected with 422 Unprocessable Entity.
Response format
Schema validation failures (400): If records fail Pydantic schema validation (missing required fields, wrong types), the API returns 400 Bad Request:
{"detail": "Field required"}Business-rule validation failures (422): If records pass schema validation but fail business-rule checks (unknown slugs, integrity errors), the API returns 422 Unprocessable Entity:
{"detail": "Record 'u1': unknown membership slug 'nonexistent'"}Common validation triggers
| Trigger | Status | Example message |
|---|---|---|
| Missing required field on account | 400 | Field required |
| Missing required field on group/license | 400 | Field required (missing id or name) |
| Unknown membership slug | 422 | Record 'u1': unknown membership slug 'nonexistent' |
| Unknown assignment slug | 422 | Record 'u1': unknown assignment slug 'nonexistent' |
| Too many records in a page | 400 | List should have at most 100 items after validation |
| Too many refs per slug | 400 | memberships['teams'] has 150 refs, max is 100. |
| Invalid field type | 400 | Input should be a valid string |
Data integrity errors (also 422)
If records pass schema validation but fail at the database level, the API returns 422 with a detail field:
{
"detail": "Sync failed for 'accounts': a required field is missing. Ensure all records have complete data."
}Common integrity error messages:
| Message | Cause |
|---|---|
a required field is missing. Ensure all records have complete data. | A database-level NOT NULL constraint was violated. Usually a missing name on a group or license referenced in memberships/assignments |
a record with the same identifier already exists. | Duplicate key in the database. Rare -- usually means concurrent writes |
a referenced record does not exist. | A foreign key reference failed. The group or license ID referenced by an account does not exist in the current session |
Sync Session Errors
After you call complete, Iden runs an asynchronous cleanup pass that marks records not included in the session as inactive. If cleanup fails, the sync session transitions to the error status.
Error response format
Poll the sync status endpoint (GET .../sync/{sync_id}/) to check for errors:
{
"sync_id": "d4e5f6a7-...",
"status": "error",
"progress": [
{"slug": "team", "name": "Team", "type": "group", "synced_count": 42},
{"slug": "account", "name": "Account", "type": "account", "synced_count": 150}
],
"error": {
"error_code": "CLEANUP_FAILED",
"message": "Cleanup task failed after maximum retries."
}
}Error codes
| Code | Description |
|---|---|
CLEANUP_FAILED | The asynchronous cleanup task failed after maximum retries. Records that were pushed successfully are preserved, but stale records were not removed. Start a new sync to retry |
Task Error Codes
When your connector cannot execute a provisioning task, it reports failure with an error code and message. These codes are passed in the error.code field when patching a task to failed status.
Standard codes
| Code | Description | Suggested resolution |
|---|---|---|
ACCOUNT_NOT_FOUND | The account does not exist in the target system | Verify the account_id in the task payload. The account may have been deleted outside of Iden. Run a sync to update Iden's records |
ACCOUNT_ALREADY_EXISTS | The account already exists (on create_account) | Check if the account was created manually or by another system. Consider reporting success if the account exists with the correct attributes |
LICENSE_EXHAUSTED | No available license seats | Purchase additional seats in the external system, or free up existing allocations. Then retry the task |
PERMISSION_DENIED | The connector's credentials lack sufficient permissions | Update the API key or service account permissions in the external system. Check that the connector has write access |
RATE_LIMITED | The external system rejected the request due to rate limiting | Wait and retry. Consider adding backoff logic to your connector. Iden may retry the task automatically |
TIMEOUT | The operation timed out before completing | Check network connectivity and the external system's health. Increase timeout thresholds in your connector if applicable |
INTERNAL_ERROR | An unspecified internal error | Use as a catch-all when no other code applies. Include a descriptive message to help administrators diagnose the issue |
Reporting format
When reporting a failed task, include both the code and a human-readable message:
{
"status": "failed",
"error": {
"code": "ACCOUNT_NOT_FOUND",
"message": "No account with ID ext-123 exists in the Jira instance."
}
}The message is displayed to administrators in the Iden dashboard. Be specific -- "User ext-123 not found in Jira" is more useful than "Not found."
409 Conflict on task updates
If you attempt to update a task that is already in completed or failed status, the API returns:
{
"detail": "Task is already in 'completed' state and cannot be updated."
}This is not an error in your connector -- it means the task was already resolved. Handle 409 gracefully by logging and moving on.
Troubleshooting
"I'm getting 409 on resource type creation"
The slug already exists for this app. This happens when:
- You already registered the type in a previous run.
- Another app in the same clone group (via
clone_from) already registered it.
Resolution: List existing resource types with GET .../resource-types/ to confirm the type exists. If it does, no action is needed -- proceed with syncing.
curl "https://developer.idenhq.com/org/acme/api/v1/bridge/apps/${APP_ID}/resource-types/" \
-H "Authorization: Api-Key <your-api-key>""My sync shows status: error"
The sync session entered an error state during the asynchronous cleanup phase.
Resolution: Check the error object in the sync status response:
response = requests.get(
f"{BASE_URL}/api/v1/bridge/apps/{app_id}/sync/{sync_id}/",
headers=HEADERS,
)
data = response.json()
if data["status"] == "error":
error = data["error"]
print(f"Error code: {error['error_code']}")
print(f"Message: {error['message']}")The most common cause is CLEANUP_FAILED, which means the cleanup task failed after retries. Records you pushed are still saved -- only the stale-record removal did not complete. Start a new sync session to retry.
"Records are disappearing after sync"
This is expected behavior. The Bridge sync uses PUT semantics: when you call complete, the set of records you pushed becomes the full truth. Any record that existed before but was not included in the session is marked inactive (removed).
Resolution: If you do not want records removed:
- Push all records every time, not just changed ones.
- Use abandon instead of complete if you are doing an incremental/partial sync. Abandoned sessions do not trigger the removal pass.
See Completion vs Abandonment in the Sync Guide for details.
"Validation errors on accounts"
Account validation errors are most commonly caused by:
- Missing email or username. Every account record must have at least one of
emailorusername. - Unknown membership or assignment slug. The
membershipsorassignmentsfield references a resource type slug that is not registered for this app. Check that you registered the group/license type before syncing accounts.
Resolution: Check the detail field in the 400/422 response for the specific record and message, then fix the offending records:
response = requests.put(
f"{BASE_URL}/api/v1/bridge/apps/{app_id}/sync/{sync_id}/account/",
headers=HEADERS,
json={"records": accounts},
)
if response.status_code in (400, 422):
print(f"Validation error: {response.json().get('detail')}")"Getting 401 Unauthorized on every request"
Resolution:
- Verify the
Authorizationheader format:Api-Key <your-api-key>(note the space afterApi-Key). - Confirm the API key is valid and has not been revoked.
- Check that you are using the correct base URL for your organization.
"Getting 403 Forbidden on a specific app"
Your API key does not have access to the requested Bridge app.
Resolution: Confirm the app was created with the same API key or that the key has been granted access to the app. List your accessible apps with GET /api/v1/bridge/apps/ to see what is available.
"Push succeeds but synced_count stays at 0"
The sync session may have been cancelled by a newer sync. Starting a new sync session automatically cancels any previous incomplete session for the same app.
Resolution: Make sure only one sync process runs per app at a time. If your connector restarts mid-sync, it will start a fresh session that cancels the previous one. Any records pushed to the cancelled session are discarded.