Error codes¶
Every error surfaced by this connector uses an OpenEHR.* reason family. Reasons are stable across versions — you can pattern-match on them in downstream Power Query steps with try … otherwise.
Quick table¶
| Reason | HTTP trigger | Meaning |
|---|---|---|
OpenEHR.AqlError |
400 | Server rejected the AQL (syntax, unknown archetype, …). |
OpenEHR.AuthError |
401, 403 | Credentials missing, wrong, or insufficient. |
OpenEHR.TimeoutError |
408 or wall-clock | Server took longer than Timeout. |
OpenEHR.ConflictError |
409 | State conflict (duplicate template, modified composition, …). |
OpenEHR.NotFoundError |
404 | Resource does not exist on the CDR. |
OpenEHR.HttpError |
any other 4xx/5xx | Unexpected HTTP error — see Details.response. |
Error record shape¶
[
Reason = "OpenEHR.AqlError",
Message = "The CDR rejected the AQL query. Verify syntax and that referenced archetypes are installed.",
Detail = [
status = 400,
request = [
method = "POST",
relativeUrl = "query/aql"
],
response = [
#"Content-Type" = "application/json",
body = "{\"error\":\"Parse error at position 42\"}"
]
]
]
Detail.response.body is raw
The response body is passed through verbatim so you can diagnose vendor-specific errors. For EHRbase, it is a JSON record with error / message; for other CDRs the shape differs.
Per-reason guidance¶
OpenEHR.AqlError¶
Almost always user-authored AQL. Common root causes:
- Missing
ASalias — columns come back as#0,#1, … stable but unnamed. CONTAINSordering — EHRbase is strict:EHR CONTAINS COMPOSITION CONTAINS OBSERVATION, not reverse.- Archetype / template not installed — upload the OPT (
dev/scripts/load-seed.sh) before querying. - Un-quoted path segments —
[at0001]node-ids and[openEHR-EHR-…]archetype-ids live in[ ]brackets.
OpenEHR.AuthError¶
Whichever auth kind is active, the token or credential is no longer valid:
flowchart TD
E["OpenEHR.AuthError"] --> K{Auth kind?}
K -->|UsernamePassword| B[Password rotated<br/>→ clear credential + re-enter]
K -->|OAuth| O{Token state?}
O -->|access_token expired| R[Should auto-refresh;<br/>if not, refresh_token is revoked]
O -->|refresh_token expired| S[Owner must re-sign-in]
O -->|scope missing| C[Grant required scope,<br/>re-consent]
Clear credentials at File → Options → Data source settings → Global permissions → Clear permissions.
OpenEHR.TimeoutError¶
Either the CDR is slow or the query is expensive. Tactics:
- Add a
WHEREclause with a time window. - Use a stored query — the CDR may plan it better than an ad-hoc body.
- Lower
PageSizeso individual round-trips are quicker (at the cost of more of them). - Raise
Timeoutif the slow page is still useful.
OpenEHR.NotFoundError¶
- Wrong base URL —
http://host:8080/ehrbase/rest/openehr/v1(no trailing slash,v1matters). - Stored-query name + version combination is not registered.
- Tenant isolation — the caller's identity does not have access to any EHRs that match the query.
OpenEHR.ConflictError¶
Vendor-specific. On EHRbase, usually:
- Uploading an OPT that already exists — idempotent
PUTis not supported on every endpoint. - Modifying a composition whose version_uid does not match.
OpenEHR.HttpError¶
Catch-all. The original HTTP status is in Detail.status. If the body is HTML (Content-Type: text/html), you are usually hitting a reverse proxy / load balancer before the CDR — 502/503/504 and a branded error page.
Pattern-matching in queries¶
let
Source = try OpenEHR.Aql(cdr, aql) otherwise #table({},{}),
Handled = if Source[HasError]? ?? false then
if Source[Error][Reason] = "OpenEHR.TimeoutError"
then #table({"Note"}, {{"Query timed out — widen WHERE or raise Timeout."}})
else if Source[Error][Reason] = "OpenEHR.AuthError"
then #table({"Note"}, {{"Auth failed — re-enter credentials."}})
else error Source[Error]
else Source[Value]
in
Handled