Skill Tracer
Skills › Scientific Agent Skills › pyzotero › error-handling.md

error-handling.md

79 lines · skills/pyzotero/references/error-handling.md at 5f2bc03

Open on GitHub
1# Error Handling
2
3Pyzotero 1.15.2 exports exceptions from `pyzotero.errors` (also from `pyzotero`). Use the current `Error` suffixes; old names such as `ResourceNotFound` do not exist.
4
5| Exception | Meaning |
6|-----------|---------|
7| `PyZoteroError` | Base class for SDK errors |
8| `UserNotAuthorisedError` | 401/403, invalid key or insufficient permissions |
9| `HTTPError` | Other mapped HTTP error |
10| `ParamNotPassedError` | Missing method argument/data |
11| `CallDoesNotExistError` | Unsupported method for this library/mode |
12| `ResourceNotFoundError` | 404, missing resource or missing full-text content |
13| `ConflictError` | 409, target library locked |
14| `PreConditionFailedError` | 412, stale version or reused write token |
15| `TooManyItemsError` | SDK batch-size check, normally over 50 |
16| `InvalidItemFieldsError` | Unknown field names |
17| `TooManyRetriesError` | SDK retries exhausted |
18| `LocalAPIKeyRequiredError` | Local write needs a valid local key |
19| `LocalAPIDeniedError` | User declined local authorization |
20| `ServerIDMismatchError` | Local database differs from cached server ID |
21| `ServerIDRequiredError` | Local write missing server-ID precondition |

Error Handling

Pyzotero 1.15.2 exports exceptions from pyzotero.errors (also from pyzotero). Use the current Error suffixes; old names such as ResourceNotFound do not exist.

ExceptionMeaning
PyZoteroErrorBase class for SDK errors
UserNotAuthorisedError401/403, invalid key or insufficient permissions
HTTPErrorOther mapped HTTP error
ParamNotPassedErrorMissing method argument/data
CallDoesNotExistErrorUnsupported method for this library/mode
ResourceNotFoundError404, missing resource or missing full-text content
ConflictError409, target library locked
PreConditionFailedError412, stale version or reused write token
TooManyItemsErrorSDK batch-size check, normally over 50
InvalidItemFieldsErrorUnknown field names
TooManyRetriesErrorSDK retries exhausted
LocalAPIKeyRequiredErrorLocal write needs a valid local key
LocalAPIDeniedErrorUser declined local authorization
ServerIDMismatchErrorLocal database differs from cached server ID
ServerIDRequiredErrorLocal write missing server-ID precondition
Raw lines 1 to 22
23## Read Errors
25```python
26import httpx2
27from pyzotero.errors import ResourceNotFoundError, UserNotAuthorisedError
29try:
30 item = zot.item('ITEMKEY')
31except ResourceNotFoundError:
32 print('Item not found')
33except UserNotAuthorisedError:
34 print('Check the key and library permissions')
35except httpx2.TransportError:
36 print('Network request failed; retry the read when connectivity returns')
37```
39Many mapped errors retain the HTTP exception in `__cause__`. Inspect response status and headers when needed, but redact credentials before logging request URLs or full exception bodies (`key_info()` embeds the key in its path).

Read Errors

import httpx2
from pyzotero.errors import ResourceNotFoundError, UserNotAuthorisedError

try:
    item = zot.item('ITEMKEY')
except ResourceNotFoundError:
    print('Item not found')
except UserNotAuthorisedError:
    print('Check the key and library permissions')
except httpx2.TransportError:
    print('Network request failed; retry the read when connectivity returns')

Many mapped errors retain the HTTP exception in __cause__. Inspect response status and headers when needed, but redact credentials before logging request URLs or full exception bodies (key_info() embeds the key in its path).

Raw lines 23 to 40
41## Version Conflicts
43```python
44from pyzotero.errors import PreConditionFailedError, ServerIDMismatchError
46try:
47 zot.update_item(item)
48except ServerIDMismatchError:
49 # Subclass of PreConditionFailedError: handle it first.
50 raise RuntimeError('Local Zotero database changed; discard cached objects and versions')
51except PreConditionFailedError:
52 fresh_item = zot.item(item['key'])
53 # Compare the original, intended, and fresh values before merging.
54 # Do not automatically overwrite a concurrent edit with new_title.
55 raise RuntimeError('Concurrent edit detected; reconcile before another write')
56```

Version Conflicts

from pyzotero.errors import PreConditionFailedError, ServerIDMismatchError

try:
    zot.update_item(item)
except ServerIDMismatchError:
    # Subclass of PreConditionFailedError: handle it first.
    raise RuntimeError('Local Zotero database changed; discard cached objects and versions')
except PreConditionFailedError:
    fresh_item = zot.item(item['key'])
    # Compare the original, intended, and fresh values before merging.
    # Do not automatically overwrite a concurrent edit with new_title.
    raise RuntimeError('Concurrent edit detected; reconcile before another write')
Raw lines 41 to 57
58## Invalid Fields
60```python
61from pyzotero.errors import InvalidItemFieldsError
63template = zot.item_template('journalArticle') # remote API only
64template['badField'] = 'bad value'
65try:
66 zot.check_items([template])
67except InvalidItemFieldsError:
68 print('Remove unknown fields before creating the item')
69```
71This validation does not replace checking `failed` in batch write JSON. HTTP 200 and `update_items()` returning `True` do not establish that every object was updated.

Invalid Fields

from pyzotero.errors import InvalidItemFieldsError

template = zot.item_template('journalArticle')  # remote API only
template['badField'] = 'bad value'
try:
    zot.check_items([template])
except InvalidItemFieldsError:
    print('Remove unknown fields before creating the item')

This validation does not replace checking failed in batch write JSON. HTTP 200 and update_items() returning True do not establish that every object was updated.

Raw lines 58 to 72
73## Rate Limits and Retry Boundaries
75Honor `Backoff` even on successful responses, and `Retry-After` on 429/503. Pyzotero's read and decorated Boolean-write paths record server delays and retry 429 responses, eventually raising `TooManyRetriesError`. Some other paths, including `create_items()`, `saved_search()`, and `new_fulltext()`, do not run that retry loop in 1.15.2: inspect the returned response/`zot.request` where applicable, and do not interpret an error payload as a valid result.
77Do not wrap all calls in a short fixed exponential retry: it can violate server delays or repeat writes whose outcome is unknown. Reconcile timeouts before replaying creates/uploads, and retry only known failures. `TooManyRequestsError` exists but is not the ordinary terminal error from the SDK's 429 retry loop. Do not retry local authorization dialogs automatically.
79Sources: [Zotero backoff rules](https://www.zotero.org/support/dev/web_api/v3/basics#rate_limiting), [Pyzotero error implementation](https://github.com/urschrei/pyzotero/blob/v1.15.2/src/pyzotero/errors.py).

Rate Limits and Retry Boundaries

Honor Backoff even on successful responses, and Retry-After on 429/503. Pyzotero's read and decorated Boolean-write paths record server delays and retry 429 responses, eventually raising TooManyRetriesError. Some other paths, including create_items(), saved_search(), and new_fulltext(), do not run that retry loop in 1.15.2: inspect the returned response/zot.request where applicable, and do not interpret an error payload as a valid result.

Do not wrap all calls in a short fixed exponential retry: it can violate server delays or repeat writes whose outcome is unknown. Reconcile timeouts before replaying creates/uploads, and retry only known failures. TooManyRequestsError exists but is not the ordinary terminal error from the SDK's 429 retry loop. Do not retry local authorization dialogs automatically.

Sources: Zotero backoff rules, Pyzotero error implementation.

Raw lines 73 to 79