Error handling
Every error ADM raises comes from one place: adm_error, and adm_ai_error for the AI
Pack. Each has a stable number, a category and a named exception, so your own code can
react to what went wrong instead of matching on message text.
The three ways to handle an ADM error
Section titled “The three ways to handle an ADM error”Pick the loosest one that does what you need — a named exception when you care about one specific failure, a category when a whole class of failures gets the same treatment.
Catch one specific error
Section titled “Catch one specific error”begin adm_document_api.trash_document(p_document_id => l_id);exception when adm_error.e_legal_hold_active then -- the document is on hold; tell the user and carry on apex_error.add_error( p_message => 'This document is under legal hold and cannot be deleted.' , p_display_location => apex_error.c_inline_in_notification );end;Branch on a category
Section titled “Branch on a category”Every code belongs to exactly one category, so you do not have to enumerate codes:
begin adm_document_api.rename_document(p_document_id => l_id, p_new_name => l_name);exception when others then if adm_error.is_no_permission(sqlcode) then log_denied_attempt(l_id); elsif adm_error.is_not_found(sqlcode) then refresh_the_grid; elsif adm_error.is_conflict(sqlcode) then suggest_another_name(l_name); else raise; end if;end;The predicates are is_not_found, is_no_permission, is_conflict, is_invalid_input
and is_invalid_state. get_category returns the category itself, and each predicate
returns a definite true or false — never null — so if not adm_error.is_not_found(...)
behaves the way it reads.
These work for AI Pack codes too, without the AI Pack being installed:
if adm_error.is_not_found(sqlcode) then ... -- true for ORA-20600 as wellTell ADM errors apart from everything else
Section titled “Tell ADM errors apart from everything else”exception when others then if adm_error.is_adm_error(sqlcode) then -- ADM refused this on purpose; the message is written for the end user show_message(adm_error.strip_ora_prefix(sqlerrm)); else raise; -- a genuine Oracle error, or another product's end if;end;is_adm_error is narrow: it is false for the bundled UC plugin libraries and for
uc_ai, even though those also raise in the -20000 .. -20999 application range. See
Number ranges.
Raising errors from your own code
Section titled “Raising errors from your own code”Hook PL/SQL and your own packages are yours: raise with plain
raise_application_error. Use a number from -20700 .. -20999, which ADM reserves
for you and will never claim:
if lower(l_document_name) like '%virus%' then raise_application_error(-20700, 'Upload aborted: the file appears to be infected.');end if;Do not raise ADM’s own codes from your code. A caller that catches
adm_error.e_doc_not_found should be able to trust that ADM raised it.
Showing errors to users in APEX
Section titled “Showing errors to users in APEX”Register the supplied error handling function on your application under Edit Application Properties → Error Handling → Error Handling Function:
adm_error.apex_error_handlerWith it in place:
- ADM errors written for end users are shown as they are, without the
ORA-nnnnn:prefix. - Errors in the
INTERNALandEXTERNALcategories are replaced by a generic sentence, because their text names internal identifiers, storage keys and HTTP endpoints. The original stays in the APEX debug log. - A unique constraint ADM owns becomes a sentence (“A document with this name already
exists in this folder.”) instead of
ORA-00001: unique constraint (…) violated. - Anything else is left to APEX, except that an internal error is replaced by the same generic sentence.
Without it, every message reaches the user raw, as ORA-20140: Document not found: 42.
Number ranges
Section titled “Number ranges”ADM shares its schema with the bundled UC plugin libraries and, for the AI Pack, with
uc_ai. All of them raise in the same -20000 .. -20999 application range, so the range
map is what makes a number attributable:
| Range | Owner |
|---|---|
-20000 .. -20099 | The bundled UC plugin libraries (uc_prp, uc_rte, uc_sbp use -20010 .. -20020; AOP uses -20001, -20002) |
-20100 .. -20299 | ADM base product — see the table below |
-20300 .. -20599 | uc_ai, the AI Pack dependency |
-20600 .. -20699 | ADM AI Pack |
-20700 .. -20999 | Yours. Hook code and anything else you write in this schema |
A bare number is not attributable on its own, which is why adm_error.is_adm_error
exists: -20012 from ADM and -20012 from a UC plugin would be indistinguishable, so ADM
does not use that range at all.
Base product codes
Section titled “Base product codes”Messages below show their apex_string.format placeholders (%0, %1, …); at runtime
those are filled in.
General
Section titled “General”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20100 | adm_error.c_err_missing_param | INVALID_INPUT | %0 is required |
ORA-20101 | adm_error.c_err_invalid_param | INVALID_INPUT | Invalid %0: %1 |
ORA-20102 | adm_error.c_err_invalid_name | INVALID_INPUT | %0 %1 |
ORA-20103 | adm_error.c_err_invalid_username | INVALID_INPUT | Username %0 |
ORA-20104 | adm_error.c_err_not_found | NOT_FOUND | %0 not found: %1 |
ORA-20105 | adm_error.c_err_already_exists | CONFLICT | %0 “%1” already exists |
ORA-20106 | adm_error.c_err_invalid_state | INVALID_STATE | %0 |
ORA-20107 | adm_error.c_err_not_supported | INVALID_INPUT | Unsupported %0: %1 |
ORA-20108 | adm_error.c_err_limit_exceeded | INVALID_INPUT | %0 (%1) exceeds the maximum of %2 |
ORA-20109 | adm_error.c_err_hook_failed | INTERNAL | There was an error processing the %0. Please contact the administrator(s). |
ORA-20110 | adm_error.c_err_internal | INTERNAL | An unexpected error occurred in %0. Please contact your administrator. |
ORA-20111 | adm_error.c_err_data_integrity | INTERNAL | Inconsistent data: %0 |
Access control, users, groups
Section titled “Access control, users, groups”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20120 | adm_error.c_err_admin_required | NO_PERMISSION | Only administrators can %0. |
ORA-20121 | adm_error.c_err_no_permission | NO_PERMISSION | You do not have permission to %0. |
ORA-20122 | adm_error.c_err_no_view_right | NO_PERMISSION | You do not have view rights on %0. |
ORA-20123 | adm_error.c_err_no_edit_right | NO_PERMISSION | You do not have edit rights on %0. |
ORA-20124 | adm_error.c_err_no_context | INVALID_STATE | No ADM session context is established. Log in through adm_context_api first. |
ORA-20125 | adm_error.c_err_invalid_credentials | NO_PERMISSION | You have entered an invalid link or password. |
ORA-20126 | adm_error.c_err_expired | NO_PERMISSION | The link has expired. Please contact the document owner. |
ORA-20127 | adm_error.c_err_token_invalid | NO_PERMISSION | The token is not valid. |
ORA-20128 | adm_error.c_err_user_not_found | NOT_FOUND | User not found: %0 |
ORA-20129 | adm_error.c_err_user_exists | CONFLICT | User already exists: %0 |
ORA-20130 | adm_error.c_err_group_not_found | NOT_FOUND | Group not found: %0 |
ORA-20131 | adm_error.c_err_role_invalid | INVALID_INPUT | Invalid role: %0 |
Documents and versions
Section titled “Documents and versions”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20140 | adm_error.c_err_doc_not_found | NOT_FOUND | Document not found: %0 |
ORA-20141 | adm_error.c_err_doc_deleted | INVALID_STATE | Document %0 has been deleted. |
ORA-20142 | adm_error.c_err_doc_exists | CONFLICT | A document with the name “%0” already exists in this folder. |
ORA-20143 | adm_error.c_err_doc_trash_rule | INVALID_STATE | %0 |
ORA-20144 | adm_error.c_err_version_not_found | NOT_FOUND | Document version not found: %0 |
ORA-20145 | adm_error.c_err_no_current_version | INVALID_STATE | Document %0 has no current version. |
ORA-20146 | adm_error.c_err_content_unavailable | INVALID_STATE | File content could not be retrieved for version %0. |
ORA-20147 | adm_error.c_err_not_an_archive | INVALID_INPUT | Document %0 is not a ZIP archive. |
ORA-20148 | adm_error.c_err_archive_empty | INVALID_STATE | Nothing to %0: %1 contains no files. |
ORA-20149 | adm_error.c_err_upload_not_found | NOT_FOUND | Upload not found or no longer available: %0 |
ORA-20150 | adm_error.c_err_upload_state | INVALID_STATE | %0 |
Folders and trash
Section titled “Folders and trash”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20170 | adm_error.c_err_folder_not_found | NOT_FOUND | Folder not found: %0 |
ORA-20171 | adm_error.c_err_folder_deleted | INVALID_STATE | Folder %0 has been deleted. |
ORA-20172 | adm_error.c_err_folder_exists | CONFLICT | A folder with the name “%0” already exists in %1. |
ORA-20173 | adm_error.c_err_folder_system | INVALID_STATE | System folders cannot be %0. |
ORA-20174 | adm_error.c_err_folder_trash_rule | INVALID_STATE | %0 |
ORA-20175 | adm_error.c_err_folder_cycle | INVALID_INPUT | A folder cannot be moved into itself or one of its own descendants. |
ORA-20176 | adm_error.c_err_folder_has_protected | INVALID_STATE | This folder contains %0 document(s) under legal hold or a retention policy, so it cannot be permanently deleted. |
Records management
Section titled “Records management”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20190 | adm_error.c_err_legal_hold_active | INVALID_STATE | Document %0 is under legal hold. |
ORA-20191 | adm_error.c_err_legal_hold_missing | INVALID_STATE | Document %0 does not have a legal hold. |
ORA-20192 | adm_error.c_err_retention_active | INVALID_STATE | Document %0 has a retention policy that has not expired. |
ORA-20193 | adm_error.c_err_retention_missing | INVALID_STATE | Document %0 does not have a retention policy. |
ORA-20194 | adm_error.c_err_retention_invalid | INVALID_INPUT | Invalid retention setting: %0 |
Sharing, link shares, embed
Section titled “Sharing, link shares, embed”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20200 | adm_error.c_err_share_not_found | NOT_FOUND | Share not found: %0 |
ORA-20201 | adm_error.c_err_share_invalid | INVALID_INPUT | Invalid share configuration: %0 |
ORA-20202 | adm_error.c_err_embed_invalid | INVALID_INPUT | Invalid embed request: %0 |
ORA-20203 | adm_error.c_err_embed_perm_invalid | INVALID_INPUT | Invalid embed permissions: %0 |
ORA-20204 | adm_error.c_err_embed_grant_unknown | INVALID_INPUT | Unknown embed grant: %0 |
Tags, annotations, metadata, taxonomy
Section titled “Tags, annotations, metadata, taxonomy”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20220 | adm_error.c_err_tag_not_found | NOT_FOUND | Tag not found: %0 |
ORA-20221 | adm_error.c_err_tag_not_assigned | INVALID_STATE | Tag %0 is not assigned to this document. |
ORA-20222 | adm_error.c_err_tag_exists | CONFLICT | This document already carries the tag %0. |
ORA-20223 | adm_error.c_err_annotation_not_found | NOT_FOUND | Annotation “%0” does not exist for document %1. |
ORA-20224 | adm_error.c_err_annotation_exists | CONFLICT | Annotation “%0” already exists for document %1. |
ORA-20226 | adm_error.c_err_tag_ambiguous | INVALID_STATE | Tag %0 is assigned to this document more than once. Address a single assignment through the document_tag_id overload. |
ORA-20225 | adm_error.c_err_taxonomy_invalid | INVALID_INPUT | Invalid taxonomy configuration: %0 |
Storage and OCI object storage
Section titled “Storage and OCI object storage”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20230 | adm_error.c_err_storage_not_configured | INVALID_STATE | Object storage is not configured: %0 is missing. |
ORA-20231 | adm_error.c_err_storage_disabled | INVALID_STATE | Object storage is not enabled. |
ORA-20232 | adm_error.c_err_storage_request | EXTERNAL | Object storage request failed: %0 |
ORA-20233 | adm_error.c_err_storage_key_missing | INVALID_STATE | Object storage key is missing for %0. |
ORA-20234 | adm_error.c_err_storage_migration | INVALID_STATE | %0 |
ORA-20235 | adm_error.c_err_policy_not_found | NOT_FOUND | Storage policy not found: %0 |
ORA-20236 | adm_error.c_err_policy_exists | CONFLICT | A storage policy already exists for %0. |
ORA-20237 | adm_error.c_err_cleanup_not_found | NOT_FOUND | Storage cleanup record not found: %0 |
ORA-20238 | adm_error.c_err_checksum_mismatch | INTERNAL | Checksum mismatch for %0. |
ORA-20239 | adm_error.c_err_storage_credentials | INVALID_STATE | No valid Web Credentials found. Please go to Shared Components > Web Credentials and set up the OCI connection. |
Settings, jobs, hooks, audit
Section titled “Settings, jobs, hooks, audit”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20260 | adm_error.c_err_setting_not_found | NOT_FOUND | Setting not found: %0 |
ORA-20261 | adm_error.c_err_setting_invalid | INVALID_INPUT | Invalid value for setting %0: %1 |
ORA-20262 | adm_error.c_err_job_failed | INTERNAL | An error occurred during the %0 job execution. Please check the logs for details. |
ORA-20263 | adm_error.c_err_aop_not_configured | INVALID_STATE | AOP is not configured. Please contact your administrator. |
ORA-20264 | adm_error.c_err_conversion_failed | EXTERNAL | %0 conversion failed. |
ORA-20265 | adm_error.c_err_search_invalid | INVALID_INPUT | Invalid search request: %0 |
AI Pack codes
Section titled “AI Pack codes”Codes here are grouped by category, in fixed sub-ranges, rather than by domain:
adm_error ships in the base product and must work with the AI Pack absent, so it works
out an AI Pack code’s category arithmetically from where the code sits instead of holding
a list of them.
| Sub-range | Category |
|---|---|
-20600 .. -20609 | NOT_FOUND |
-20610 .. -20624 | INVALID_INPUT |
-20625 .. -20634 | INVALID_STATE |
-20635 .. -20644 | EXTERNAL |
-20645 .. -20649 | INTERNAL |
NOT_FOUND
Section titled “NOT_FOUND”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20600 | adm_ai_error.c_err_collection_not_found | NOT_FOUND | RAG collection not found: %0 |
ORA-20601 | adm_ai_error.c_err_chunk_not_found | NOT_FOUND | RAG chunk not found: %0 |
ORA-20602 | adm_ai_error.c_err_queue_entry_not_found | NOT_FOUND | No RAG job queue entry for %0 |
ORA-20603 | adm_ai_error.c_err_debug_log_not_found | NOT_FOUND | RAG debug log entry not found: %0 |
INVALID_INPUT
Section titled “INVALID_INPUT”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20610 | adm_ai_error.c_err_config_invalid | INVALID_INPUT | Invalid %0: %1 |
ORA-20611 | adm_ai_error.c_err_config_missing | INVALID_INPUT | %0 requires %1 |
ORA-20612 | adm_ai_error.c_err_config_not_json | INVALID_INPUT | The configuration is not valid JSON. |
ORA-20613 | adm_ai_error.c_err_chunk_config | INVALID_INPUT | Invalid chunk configuration: %0 |
ORA-20614 | adm_ai_error.c_err_unsupported_value | INVALID_INPUT | Unsupported %0: “%1”. Allowed values are %2. |
ORA-20615 | adm_ai_error.c_err_missing_param | INVALID_INPUT | %0 is required |
INVALID_STATE
Section titled “INVALID_STATE”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20625 | adm_ai_error.c_err_no_vector_support | INVALID_STATE | The native Oracle vector store needs Oracle 23ai or newer. This database is %0. Use the Qdrant vector store instead. |
ORA-20626 | adm_ai_error.c_err_vector_store | INVALID_STATE | The vector store is not usable: %0 |
ORA-20627 | adm_ai_error.c_err_no_content | INVALID_STATE | There is no content to work with: %0 |
ORA-20628 | adm_ai_error.c_err_wrong_job_type | INVALID_STATE | A %0 job was queued but the collection has no %1 configuration. |
ORA-20629 | adm_ai_error.c_err_vector_dims_immutable | INVALID_STATE | The vector store for collection %0 was built with %1 dimensions and cannot be changed to %2. Recreate the collection. |
EXTERNAL
Section titled “EXTERNAL”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20635 | adm_ai_error.c_err_qdrant_request | EXTERNAL | The Qdrant request failed: %0 |
ORA-20636 | adm_ai_error.c_err_embedding | EXTERNAL | Embedding failed: %0 |
ORA-20637 | adm_ai_error.c_err_extraction | EXTERNAL | Text extraction failed: %0 |
ORA-20638 | adm_ai_error.c_err_extraction_truncated | EXTERNAL | The model stopped before the document ended (finish_reason=length). Raise the token limit or split the document. |
ORA-20639 | adm_ai_error.c_err_generation | EXTERNAL | Answer generation failed: %0 |
INTERNAL
Section titled “INTERNAL”| Code | Constant | Category | Message |
|---|---|---|---|
ORA-20645 | adm_ai_error.c_err_internal | INTERNAL | An unexpected error occurred in %0. Please contact your administrator. |
ORA-20646 | adm_ai_error.c_err_job_failed | INTERNAL | An error occurred during the %0 job execution. Please check the logs for details. |
Categories
Section titled “Categories”| Category | Means | Typical response |
|---|---|---|
NOT_FOUND | The thing was not there, or is deleted | Refresh, or tell the user it is gone |
NO_PERMISSION | The current user may not do this | Hide the action; log the attempt |
INVALID_INPUT | The arguments were wrong | Show the message; it names what to fix |
INVALID_STATE | The object exists but not in a usable state | Show the message; the state has to change first |
CONFLICT | Something with that name or key already exists | Offer another name, or merge |
EXTERNAL | A service outside the database failed (object storage, Qdrant, an LLM) | Retry; the detail is in the log |
INTERNAL | A bug or a broken invariant | Show nothing specific; read the log |
adm_error.apex_error_handler replaces EXTERNAL and INTERNAL messages before a user
sees them.
Upgrading from 0.1.6 or earlier
Section titled “Upgrading from 0.1.6 or earlier”Error codes were renumbered in 26.1, and code that catches ADM errors by number has to be updated. What changed, what to replace it with, and where hook code should raise its own errors is in What changed in 26.1 → Errors.
Also refer to
Section titled “Also refer to”- Hooks — raising your own errors from hook code
- Extensibility
- Upgrading
adm_error