Retry safety
Orion retries a task in more places than a workflow author has in mind. The trace DLQ replays a failed async delivery,
a Kafka redelivery re-runs everything after an uncommitted offset, and
http_call retries its own transport failures. Whether that is harmless
depends on the function.
Description
This is a different question from whether an error was transient. Orion
already classifies that per error — a connection failure is retryable, a
rejected query is not. The table below answers the other half: if the retry
happens, what does it cost?
GET /api/v1/admin/functions serves the same answer per function as
retry_safety, so tooling can read it rather than hard-coding this table.
Answers
| Answer | Meaning |
|---|---|
pure | No effect outside the message. Free to retry. |
read | Observes state without changing it. A retry costs a round trip and may see a newer value. |
idempotent_write | Writes, but a second run lands the same end state. |
unsafe_write | Writes, and a second run duplicates the effect — the second email, the second record. |
depends_on | The task decides, and the answer carries the input to look at. |
Functions
| Function | Retry safety | Notes |
|---|---|---|
crypto | pure | Local computation. |
jwt_sign | pure | Local signing. |
model_infer | pure | The graph is a function of its inputs and its weights; a retry re-runs it and lands the same tensors. |
storage_presign | pure | SigV4 arithmetic over the connector’s credentials; zero bytes move. |
cache_read | read | |
db_read | read | |
data_query | read | |
mongo_read | read | |
mongo_aggregate | read | Aggregation pipelines with $out/$merge are refused by the stage allowlist, so this stays a read. |
storage_head | read | Metadata only. |
jwt_verify | read | May fetch a JWKS document; the cache usually answers. |
cache_write | idempotent_write | The same key and value land the same entry. A ttl restarts from the retry. |
send_email | unsafe_write | A retry sends a second message. |
publish_kafka | unsafe_write | A retry publishes a second record. Consumers that need exactly once delivery should dedupe on a key the workflow sets. |
http_call | depends_on method | GET/HEAD are safe; POST/PATCH may already have been applied. This is what the connector’s retry_non_idempotent flag is about — off by default. |
db_write | depends_on sql | Raw SQL: an UPDATE … SET x = 1 is idempotent, an INSERT is not. |
data_write | depends_on op | upsert and delete are idempotent; insert is not; update depends on the expression. |
mongo_write | depends_on op | Same split: an upsert repeats safely, an insert does not. |
channel_call | depends_on channel | The answer is whatever the target channel’s workflow does. |
Every engine built-in (map, filter, parse_json, …) is pure: they read
and write the message and nothing else. log writes to this node’s own
observability output, which a retry repeating is not a duplicated effect in the
sense above.
Related
- Task functions: every function, with its page.
- Timeouts, retries and circuit breakers: where Orion retries, and what it deliberately does not re-drive.
- Traces and async processing: the trace DLQ replay that re-runs a delivery.
- Kafka channels: the redelivery a consumer must expect.
Last verified 14 September 2026