Errors and timeouts
When DarkRay refuses a request, or can’t be reached, the client raises an exception. Each one carries a sentence saying what went wrong and what to do next.
Catch errors
Section titled “Catch errors”Every exception is a DarkRayError, with three attributes:
message: the plain sentence, such as “Photo 1234 isn’t in the catalog Travel.”code: the bridge’s error code, orNonefor an error raised on your side of the connection.data: details a program can use (a field’s range, the ids not found), orNone.
A call that changes many photos checks every id and value first, and changes all of them or none. So when a call raises, nothing changed:
from darkray_bridge import ControlOff, DarkRay, DarkRayError, NotRunning, OutOfRange
try: with DarkRay.connect() as app: ids = [photo.id for photo in app.iter_photos("camera:X-T5 rating:>=3")] try: app.set_edits(ids, values={"exposure_ev": 6.0}) except OutOfRange as err: # Nothing changed. err.data names the field and its range. app.set_edits(ids, values={"exposure_ev": err.data["max"]})except (NotRunning, ControlOff) as err: print(err.message) # says what to do: start DarkRay, or turn MCP onexcept DarkRayError as err: print(f"DarkRay refused: {err.message} ({err.code}, {err.data})")The exceptions
Section titled “The exceptions”All are importable from darkray_bridge, except ProtocolError, which lives in
darkray_bridge.errors.
| Exception | Raised when | data |
|---|---|---|
NotRunning |
DarkRay isn’t running: no discovery file, one left by a DarkRay that has quit, or nothing answering on its port | |
ControlOff |
DarkRay runs with MCP off | |
Unauthorized |
DarkRay refused the connection’s token; connect again | |
TimedOut |
DarkRay didn’t answer in time (see below) | |
Disconnected |
The connection closed while a call waited: DarkRay quit, or MCP was turned off | |
ProtocolError |
A message that wasn’t JSON-RPC, or that DarkRay couldn’t read | |
UnknownMethod |
The running DarkRay is older than your darkray_bridge and lacks this MCP tool |
method |
InvalidParams |
A missing, unknown or mistyped parameter, a search DarkRay can’t read, or an unknown edit field | param |
NotFound |
A catalog that isn’t open, or a photo, collection, preset, look or job that doesn’t exist | kind, ids |
OutOfRange |
A value outside its field’s range, or not among its choices | field with min and max, or choices |
TooMany |
Past a limit: 1,000 photos per call, 500 results per page | limit, param |
NotAllowed |
The request can’t run now or here: another catalog is shown, the photo in Edit stayed busy, another merge or import is running, a smart collection, edits saved by a newer DarkRay, photos that aren’t RAWs (merges), an export folder that’s missing, content search not set up | reason, such as catalog, busy, smart, newer, not_raw, export, content |
Failed |
The work started and failed, or DarkRay hit an internal error; also raised by Job.wait() when the job fails |
reason |
An error code this client doesn’t know raises a plain DarkRayError with that code.
Timeouts
Section titled “Timeouts”DarkRay.connect(timeout=10)waits up to 10 seconds to connect and say hello.- Each call then waits up to 120 seconds for DarkRay’s answer, then raises
TimedOut. DarkRay may still finish the work; the client only stops waiting. - A call that would change the photo open in Edit while you drag a control, paint a stroke,
or the photo loads waits for that to end, and fails with
NotAllowedafter 20 seconds. - Exports, imports and merges don’t block a call: they return a job after at most
20 seconds.
job.wait(timeout=…)sets your own limit (see Jobs and previews).
To let every call wait longer, or as long as it takes (None), build the client from a
connection yourself:
from darkray_bridge import BridgeConnection, DarkRay
app = DarkRay(BridgeConnection.open("my script"), timeout=600)Technical details: errors from call() and Job
app.call(name, ...) raises KeyError for a name that isn’t an MCP tool in your copy of
darkray_bridge. The error codes behind each exception are listed in
The bridge protocol.