Every refusal the API gives carries a stable code and a message in words. The screens show the message; the code is what support and scripts rely on. This page lists every code, grouped by the first part of the code, with what it means where the source explains it.
71 codes.
Something went wrong on the server (internal.*)#
Not caused by anything you did. Retry; if it repeats, give the request id (shown with the error) to your administrator.
| Code | Constant | What it means |
|---|
internal.unknown | INTERNAL | An unexpected error on the server. The request id shown with it identifies the entry in the server log. |
internal.not_implemented | NOT_IMPLEMENTED | The operation exists in the contract but is not built. |
internal.service_unavailable | SERVICE_UNAVAILABLE | The service, or something it depends on such as the database, could not answer. Retry shortly. |
The request was not acceptable (validation.*)#
Fix the value the message names. Nothing was saved.
| Code | Constant | What it means |
|---|
validation.failed | VALIDATION_FAILED | A value was missing, malformed or outside what is allowed. The message names the field. Nothing was saved. |
validation.blocked | VALIDATION_BLOCKED | A save refused by a check the field's design says blocks (ADR 0016, decision 19): details.fields lists each field and cell that failed, with the check's message. Nothing was written. |
validation.invalid_input | INVALID_INPUT | The request body or query could not be read. |
validation.invalid_state_transition | INVALID_STATE_TRANSITION | The action is not allowed from the current status (for example a query that is Escalated cannot be reopened). The message names the status and the action. |
Signing in (auth.*)#
Ask for a new code, or sign in again.
| Code | Constant | What it means |
|---|
auth.required | AUTH_REQUIRED | There is no signed-in session. Sign in again. |
auth.invalid_credentials | AUTH_INVALID_CREDENTIALS | The one-time code is wrong or has expired. Ask for a new one. |
auth.token_invalid | AUTH_TOKEN_INVALID | The session token is not valid (it was altered, or belongs to a session that has ended). Sign in again. |
auth.token_expired | AUTH_TOKEN_EXPIRED | The session token has expired. Sign in again. |
auth.refresh_superseded | AUTH_REFRESH_SUPERSEDED | The refresh token was exchanged a moment ago by another request (a second tab, another BFF instance). The session is alive; the caller holds a stale copy of it and should retry with the renewed one — not sign the user out. |
auth.account_locked | AUTH_ACCOUNT_LOCKED | The code has been tried the allowed number of times. Ask for a new code; the sign-in page then pauses this browser window for a while. |
auth.account_disabled | AUTH_ACCOUNT_DISABLED | The person is suspended in this organization. |
Not allowed (authz.*)#
Your role does not hold the permission, or the record belongs to another site or study. A person with the right role can do it.
| Code | Constant | What it means |
|---|
authz.forbidden | FORBIDDEN | The action is not allowed for this person. |
authz.insufficient_permissions | INSUFFICIENT_PERMISSIONS | The person's role does not hold a permission the action needs; the message names it. |
authz.scope_mismatch | SCOPE_MISMATCH | The person holds the permission, but not at the level (organization, study or site) the action needs. |
authz.site_access_denied | SITE_ACCESS_DENIED | The record belongs to a site the person does not work at. |
authz.study_access_denied | STUDY_ACCESS_DENIED | The person holds no role on this study. |
The thing is not there, or is in the way (resource.*)#
Not found, already exists, a conflict with the current state, locked or gone.
| Code | Constant | What it means |
|---|
resource.not_found | NOT_FOUND | There is no such record, or it belongs to a site or workspace the person cannot see (the system does not say which). |
resource.already_exists | ALREADY_EXISTS | A record with the same identity already exists. |
resource.conflict | CONFLICT | The action conflicts with the current state (for example two people changed the same thing at the same time). Reload and retry. |
resource.locked | LOCKED | The record is locked and cannot be changed. |
resource.gone | GONE | The record existed and is no longer available. |
The study's state or design (study.*)#
The study is in a state that does not allow this, or the design blocks it.
| Code | Constant | What it means |
|---|
study.not_live | STUDY_NOT_LIVE | The study has no Live version yet, so nothing production can be done with it. |
study.state_not_allowed | STUDY_STATE_NOT_ALLOWED | The study's operational state does not allow this write; details carries the state, intent and workspace. |
study.no_project_manager | STUDY_NO_PROJECT_MANAGER | A study needs a Project Manager who can staff it (ADR 0018, decisions 15 and 23): the role picked for them is not a study role, or does not carry team.assign, so nobody could build the team. details.reason says which. |
study.version_in_flight | STUDY_VERSION_IN_FLIGHT | Completing a study with a version still being built or tested: withdraw it first. |
study.readiness_changed | STUDY_READINESS_CHANGED | The pre-lock listing a signer saw is not the one the server would show now; details.readiness is the fresh one. |
study.lock_blocked | STUDY_LOCK_BLOCKED | A lock was asked for while a query is open or a serious event is overdue or unreconciled (F-049). |
sdv (sdv.*)#
| Code | Constant | What it means |
|---|
sdv.plan_live_only | SDV_PLAN_LIVE_ONLY | The SDV plan is the study's monitoring plan, and once the study has left Setup it is edited from Live: a UAT cycle (an amendment's) never decides what a monitor verifies in production (P1-M-31, ADR 0014 decision 8). |
sdv.plan_unresolved | SDV_PLAN_UNRESOLVED | A version cannot go Live because the SDV plan names a form, a field or a visit it does not have: the plan is carried into the version that goes Live by code, and a requirement with no counterpart would silently stop applying (P1-M-38, ADR 0014 amendment 2026-10-11). |
workspace (workspace.*)#
| Code | Constant | What it means |
|---|
workspace.required | WORKSPACE_REQUIRED | The route acts in a workspace (UAT's or Live's data) and the session has not entered one (ADR 0019): open a version's workspace first. A session is global until a version workspace is entered; nothing a workspace owns is read or written without one. |
workspace.mismatch | WORKSPACE_MISMATCH | The request asserts the workspace the caller believes it is in (X-Workspace) and the session is in another, or in none (ADR 0019, AW.4): another tab entered a different workspace, or the session ended there. The header is an assertion, never a selector: nothing is read or written. details.claimed is what the caller said, details.actual what the session is in (null: none): reload, and read where you are. |
version (version.*)#
| Code | Constant | What it means |
|---|
version.frozen | VERSION_FROZEN | A configuration write for a version that is not in Build (SQLSTATE P0021). |
version.revision_stale | VERSION_REVISION_STALE | A save made stale by a later revision of the same entity; details.conflicts lists what changed (SQLSTATE P0026). |
version.in_flight_exists | VERSION_IN_FLIGHT_EXISTS | A study has one version in flight at a time; details.versionId names it. |
version.signoff_refused | VERSION_SIGNOFF_REFUSED | A sign-off the database refuses: one person in two roles, a role signed twice, a binding that no longer holds (SQLSTATE P0023). |
version.build_invalid | VERSION_BUILD_INVALID | A version cannot go to UAT: details.problems lists every reason. |
version.breaking_change | VERSION_BREAKING_CHANGE | An amendment changes something that would no longer fit the Live data already captured: an item's data type, unit or code-list values, or an event form's kind (ADR 0016, decision 8). Refused at the submit to UAT and again at go-live; details.changes lists each (the form, the variable, what changed) and details.instead says what to do. |
version.snapshot_missing | VERSION_SNAPSHOT_MISSING | A version that should run but has no snapshot to run from: a broken state, refused rather than run against no configuration (the schema keeps it from arising). |
uat (uat.*)#
| Code | Constant | What it means |
|---|
uat.cycle_closed | UAT_CYCLE_CLOSED | A UAT write whose cycle is not open, or whose version is not in UAT (SQLSTATE P0022). |
uat.workspace_required | UAT_WORKSPACE_REQUIRED | The issue log of a UAT cycle is the UAT workspace's: asked from another workspace, it is refused (ADR 0014, decision 18). |
uat.critical_issue_open | UAT_CRITICAL_ISSUE_OPEN | A cycle with a critical issue still open cannot pass: resolve it, or accept it with a reason (SQLSTATE P0023; ADR 0014, decision 18). |
record (record.*)#
| Code | Constant | What it means |
|---|
record.stale | RECORD_STALE | A save of a record made against a version of it that has since changed (ADR 0015, decision 4; SQLSTATE P0028). Nothing was written; details.changed lists the coordinates whose current value was written after the version the client read. |
A clinical rule (clinical.*)#
A separation of duties or a data-integrity rule refused the action; the message says which.
| Code | Constant | What it means |
|---|
clinical.separation_of_duties | CLINICAL_SEPARATION_OF_DUTIES | The person who entered a value cannot verify it, and one who raised or answered a query cannot close it (ADR 0015, decisions 7 and 8; SQLSTATE P0027). The database refuses it for every writer; details.kind says which act. |
| Code | Constant | What it means |
|---|
form.locked | FORM_LOCKED | The record has been signed and locked; it cannot be changed through this action. |
form.requires_unlock | FORM_REQUIRES_UNLOCK | A locked record must be unlocked before it can be changed. |
subject (subject.*)#
| Code | Constant | What it means |
|---|
subject.locked | SUBJECT_LOCKED | The participant's record is locked. |
database (database.*)#
| Code | Constant | What it means |
|---|
database.locked | DATABASE_LOCKED | The study database is locked (or the study is archived and read-only). Nothing can be written until it is unlocked. |
signature (signature.*)#
| Code | Constant | What it means |
|---|
signature.required | SIGNATURE_REQUIRED | The action needs an electronic signature. |
signature.invalid | SIGNATURE_INVALID | The signature could not be applied: the code, the typed name or the signer did not match. |
signature.not_offered | SIGNATURE_NOT_OFFERED | The version does not ask for a signature on a visit or a casebook, so none is offered (ADR 0016, decision 16). |
signature.not_signable | SIGNATURE_NOT_SIGNABLE | A visit or a casebook that cannot be signed yet: details.problems lists everything that is in the way (ADR 0016, decision 17). |
signature.already_signed | SIGNATURE_ALREADY_SIGNED | A visit or a casebook that already has a signature standing: it is signed again after a change withdrew it (SQLSTATE P0032). |
audit (audit.*)#
| Code | Constant | What it means |
|---|
audit.reason_required | AUDIT_REASON_REQUIRED | A reason for the change is required and was not given. |
org (org.*)#
| Code | Constant | What it means |
|---|
org.last_admin | ORG_LAST_ADMIN | The change would leave the organization with no Organization Admin who can act. |
team (team.*)#
| Code | Constant | What it means |
|---|
team.separation_rule | TEAM_SEPARATION_RULE | An organization member on a site team, or site personnel on the organization team (or an external study member who is one). |
team.site_closed | TEAM_SITE_CLOSED | A site that is closed takes no team. |
team.delegation_signer | TEAM_DELEGATION_SIGNER | A delegation entry signed by someone who may not sign it: the delegate, or a person off the site's team. |
access (access.*)#
| Code | Constant | What it means |
|---|
access.exception_refused | ACCESS_EXCEPTION_REFUSED | A scoped access exception that a guardrail, an approver rule or the database refuses; details.kind says which. |
rbac (rbac.*)#
| Code | Constant | What it means |
|---|
rbac.guardrail_violation | ROLE_GUARDRAIL_VIOLATION | A role breaks a hard guardrail. Nobody can override it. |
rbac.warnings_unacknowledged | ROLE_WARNINGS_UNACKNOWLEDGED | A role raises soft warnings that have no override with a reason. |
rbac.role_set_refused | ROLE_SET_REFUSED | A set of roles submitted together (the onboarding wizard, ADR 0018, decision 17) has at least one that cannot be created: a hard error, a warning with no reason, a name already taken or repeated in the set. Nothing was created; details.roles lists each role that has a problem, by its position in the set. |
visit (visit.*)#
| Code | Constant | What it means |
|---|
visit.not_yet_open | VISIT_NOT_YET_OPEN | The visit's window has not opened. Data entry needs an early-access exception with a reason. |
rate (rate.*)#
| Code | Constant | What it means |
|---|
rate.limited | RATE_LIMITED | Too many requests in a short time from this client. Wait the number of seconds shown and retry. |
rate.quota_exceeded | QUOTA_EXCEEDED | A usage quota was exceeded. |
upstream (upstream.*)#
| Code | Constant | What it means |
|---|
upstream.timeout | UPSTREAM_TIMEOUT | A service the server depends on (mail, the object store, the AI provider) did not answer in time. |
upstream.unavailable | UPSTREAM_UNAVAILABLE | A service the server depends on is not reachable. |
upstream.bad_response | UPSTREAM_BAD_RESPONSE | A service the server depends on answered with something the server could not use. |
ai (ai.*)#
| Code | Constant | What it means |
|---|
ai.unavailable | AI_UNAVAILABLE | An optional AI helper that cannot answer: no provider is configured, or the provider failed. The screen it belongs to works without it (ADR 0018, decision 20); details.reason says not_configured or provider_error. |
API reference · Who can and cannot