Error codes¶
How failure is reported depends on which library you use:
Calls throw a subtype of SolidException. The hierarchy is sealed, so a when over it is
exhaustive.
Calls return SolidResult<T>. Unwrap with getOrThrow() to get the same exceptions, or match
on SolidResult.Failure to handle them without throwing.
The hierarchy¶
SolidException
├── SolidAppNotFoundException — Android Solid Services is not installed
├── SolidServiceConnectionException — the IPC binding failed or dropped
├── SolidNotLoggedInException — no account signed in for that WebID
├── SolidServicesDrawPermissionDeniedException — legacy; only the removed requestLogin path
├── SolidResourceException
│ ├── NotSupportedClassException — class does not extend RDFResource/NonRDFResource
│ ├── NotPermissionException — your app has no grant for this account
│ ├── NullWebIdException — no WebID supplied for the call
│ └── UnknownException — unexpected server or protocol error
└── SolidSharingException
├── AccessDeniedException — the pod refused the access change
├── NoInboxException — the receiver advertises no inbox
├── InboxUnauthorizedException — their inbox needs credentials you do not have
├── InboxForbiddenException — their inbox refused your post
├── NotificationDeliveryException — the notification could not be delivered
├── ImpersonationDetectedException — a notification claimed a sender it cannot prove
├── StaleAclException — the ACL changed under a conditional write
└── UnsupportedAuthBackendException — the server speaks neither WAC nor ACP
What to do about each¶
Setup and connection¶
| Exception | Cause | Fix |
|---|---|---|
SolidAppNotFoundException |
the host app is not on the device | prompt to install it, or switch to the api library |
SolidServiceConnectionException |
the binding failed, or the host app was stopped or updated | the connector rebinds itself; collect the connection-state flow and retry once it emits true |
SolidNotLoggedInException |
no session for that WebID — signed out, or removed in Settings | send the user back through sign-in |
NullWebIdException |
a call was made with no WebID | keep the WebID you got at sign-in and pass it to every call |
Permission¶
| Exception | Cause | Fix |
|---|---|---|
NotPermissionException |
the user has not granted your app access to this account | ask again; the grant dialog is shown by the host app, and the user can revoke at any time |
AccessDeniedException |
the pod refused the access change you asked for | usually you are not the owner, or the server enforces a policy you cannot override |
A bare 403 from a write |
signed in, but without write access there | see Sharing — the owner has to grant it |
A failure is not a denial
probeAccess throws on anything indeterminate — a 401 blip, a 5xx, a dropped connection —
rather than returning Denied. Treating those as "no access" hides controls the user is
actually allowed to use.
Writes and concurrency¶
| Symptom | Cause | Fix |
|---|---|---|
412 Precondition Failed |
someone else wrote between your read and your write | re-read, merge or ask the user, then write again |
StaleAclException |
the ACL moved under a conditional access change | re-read the current shares and reapply |
404 on a write |
the parent container does not exist | call ensureContainer first |
TransactionTooLargeException |
a body over roughly 1 MB crossed Binder | use writeStream/readStream, which pass a file descriptor instead |
Sharing and the inbox¶
| Exception | Cause | Fix |
|---|---|---|
NoInboxException |
the receiver's profile advertises no inbox | the share still works; only the notification cannot be delivered |
InboxUnauthorizedException / InboxForbiddenException |
their inbox rejected the post | nothing to fix on your side — treat delivery as best-effort |
NotificationDeliveryException |
delivery failed in transit | the access change already happened; retry the notification only |
ImpersonationDetectedException |
an inbox message claimed a sender it could not prove | drop it — this is the anti-spoofing gate doing its job |
UnsupportedAuthBackendException |
the server speaks neither WAC nor ACP | sharing is not available against that server |
Content and protocol¶
| Exception | Cause | Fix |
|---|---|---|
NotSupportedClassException |
the class passed to read/create extends neither base type |
extend RDFResource or NonRDFResource |
UnsupportedRdfContentTypeException |
the server answered Turtle | reads negotiate for JSON-LD or N-Triples; see the notes under Resources |
UnknownException |
anything unclassified | check the message — it carries the server's response |
Server-specific behaviour¶
Some failures are a particular pod server rather than your code. The ones already handled for you, and the ones that are not, are in Troubleshooting.