For the complete documentation index, see llms.txt.
Skip to main content
Version: 8.10 (unreleased)

Runtime

Technical Preview

The Go SDK is a technical preview. Its API surface may still evolve and changes may not follow semantic versioning. Pin an exact version if you need stability.

Error types returned by every SDK call, the helpers that classify them, and the polling helper that absorbs eventual consistency.

APIError​

APIError is returned when the server responds with a non-success HTTP status. It carries the status code and the (often RFC 7807 problem-detail) response body.

Fields​

FieldTypeDescription
StatusintStatus is the HTTP status code.
BodystringBody is the raw response body, if any.

Methods​

Error​

func (e *APIError) Error() string

BpmnError​

BpmnError is an error that, when returned by a JobHandler, makes the worker throw a BPMN error (raising a catch event) instead of failing the job.

Fields​

FieldTypeDescription
Codestring
Messagestring
Variablesmap[string]any

Methods​

Error​

func (e *BpmnError) Error() string

Clock​

Clock is time and waiting, as an injectable dependency.

It exists so that runtime cadence can be resolved through a clock a test controls rather than by calling the time package directly. Inject one with WithClock; the default is LiveClock.

Runtime call sites are being migrated onto it (see camunda/orchestration-cluster-api-go#40); until that lands, an injected clock is stored and reachable via CamundaClient.Clock but does not yet drive retry, backpressure, worker or consistency cadence.

Implementations must be safe for concurrent use.

ClockController​

ClockController is the engine-side clock an EngineClock drives.

Implemented by CamundaClient in terms of PUT /clock and POST /clock/reset. It is an interface so the pin semantics can be tested without a running engine.

EngineClock​

EngineClock is a clock bound to the engine's own clock.

A wait does not pass time locally: it moves the engine forward and reports the new instant. Process instances, timers and the SDK therefore agree on what time it is, which a purely local test clock cannot achieve.

A wait resolves against an instant read before the engine is contacted, so waits that overlap -- those that read the clock before any of them lands -- settle at a single instant instead of summing. A wait that begins after an earlier one has landed reads the new time and composes from it, which is the intended behaviour: it really did start later.

Clock pinning is an alpha engine endpoint intended for tests, not production clusters. Pass the client the pin requests should travel on; it keeps real time, so the requests themselves are unaffected by the pinning.

Functions​

NewEngineClock​

func NewEngineClock(engine ClockController) *EngineClock

NewEngineClock binds to an engine. The clock starts unpinned, following real time until the first wait or EngineClock.PinTo.

engine must not be a client using this clock: a client captures its clock when it is built, so the one passed here always predates this clock.

Methods​

After​

func (c *EngineClock) After(d time.Duration) <-chan time.Time

After returns immediately with a channel that receives once the engine clock has been advanced by d. The advance runs in the background, so After stays usable in a select rather than blocking the caller for an engine round-trip.

A failed pin panics in that goroutine. Because the panic is not on the caller's goroutine it cannot be recovered, and terminates the program: After has no way to report an error, and reporting a time the engine never moved to would be worse. The panic value is an error wrapping the cause so the crash names it.

Use EngineClock.Sleep wherever failure needs handling; it returns the error.

IsPinned​

func (c *EngineClock) IsPinned() bool

IsPinned reports whether this clock currently holds the engine clock pinned.

Now​

func (c *EngineClock) Now() time.Time

Now reports the pinned instant, or live time when unpinned.

PinTo​

func (c *EngineClock) PinTo(ctx context.Context, t time.Time) error

PinTo moves the engine clock to an absolute instant, and reports that instant from EngineClock.Now once the engine accepts it -- including when t is in the past, since the SDK's reading has to match the engine's.

A no-op when the clock already sits at or past t, which is what makes overlapping waits settle at a single instant. The local reading is published only after the engine accepts the pin, so a failed request leaves the clock untouched.

Waits never move the clock backwards: EngineClock.Sleep derives its instant by adding to the current reading.

Reset​

func (c *EngineClock) Reset(ctx context.Context) error

Reset returns the engine to real time. Readings follow live time again afterwards, rather than freezing at the last pinned instant.

Sleep​

func (c *EngineClock) Sleep(ctx context.Context, d time.Duration) error

Sleep advances the engine clock by d rather than waiting for it to pass.

LiveClock​

LiveClock is real time, backed by the time package. It is the clock used when none is injected.

Methods​

After​

func (LiveClock) After(d time.Duration) <-chan time.Time

After returns a channel that receives once d has elapsed.

Now​

func (LiveClock) Now() time.Time

Now reports the current system time.

Sleep​

func (LiveClock) Sleep(ctx context.Context, d time.Duration) error

Sleep waits for d or until ctx is canceled.

PollOption​

type PollOption func(*pollConfig)

PollOption customizes Poll.

Functions​

WithPollClock​

func WithPollClock(clock Clock) PollOption

WithPollClock resolves the poll interval and timeout through clock. Poll is a package-level function with no client to inherit one from, so pass the client's clock (CamundaClient.Clock) to keep a test on a single timeline.

WithPollRetryInterval​

func WithPollRetryInterval(d time.Duration) PollOption

WithPollRetryInterval sets the delay between polling attempts.

WithPollTimeout​

func WithPollTimeout(d time.Duration) PollOption

WithPollTimeout sets the overall polling deadline.

WithRetryOn​

func WithRetryOn(pred func(error) bool) PollOption

WithRetryOn overrides the predicate that decides whether an error is retryable (the entity is not yet consistent). The default retries on 404.

Package functions​

IsEventuallyConsistent​

func IsEventuallyConsistent(operationID string) bool

IsEventuallyConsistent reports whether the REST operation with the given operationId is eventually consistent: a read issued immediately after a related write may not observe the write yet. Wrap such reads in Poll to tolerate propagation delay.

The operationId is the OpenAPI operation id (camelCase), e.g. "getProcessInstance". The set is generated from the spec metadata.

IsNotFound​

func IsNotFound(err error) bool

IsNotFound reports whether err is (or wraps) an *APIError with HTTP 404.

Poll​

func Poll[T any](ctx context.Context, fn func(context.Context) (T, error), opts ...PollOption) (T, error)

Poll repeatedly calls fn until it succeeds, the retry predicate returns false, the timeout elapses, or ctx is canceled. It is intended for eventually-consistent reads: newly created or modified entities may not be immediately visible in the cluster's secondary storage, surfacing as a 404.

By default Poll retries while fn returns a 404 and gives up after the timeout with ErrEventualConsistencyTimeout (wrapping the last error). A non-retryable error is returned immediately.

Example:

pi, err := camunda.Poll(ctx, func(ctx context.Context) (*camunda.ProcessInstanceResult, error) {
return client.GetProcessInstance(ctx, key)
})

StatusCode​

func StatusCode(err error) (status int, ok bool)

StatusCode returns the HTTP status code carried by err if it is (or wraps) an *APIError, and ok reports whether it was found.

ErrConfig, ErrAuth, ErrBackpressureQueueFull, ErrEventualConsistencyTimeout, ErrLeaseNotHonored​

var (
// ErrConfig indicates configuration was invalid or incomplete.
ErrConfig = errors.New("camunda: configuration error")
// ErrAuth indicates a failure obtaining or refreshing an auth token.
ErrAuth = errors.New("camunda: authentication error")
// ErrBackpressureQueueFull indicates the client-side backpressure controller
// rejected the request because its waiter queue is at capacity. It is the same
// value the backpressure gate returns, so errors.Is matches it on any request
// rejected for this reason (facade, Raw client, or job workers).
ErrBackpressureQueueFull = backpressure.ErrQueueFull
// ErrEventualConsistencyTimeout indicates an eventual-consistency polling
// helper timed out before its predicate was met.
ErrEventualConsistencyTimeout = errors.New("camunda: eventual consistency timeout")
// ErrLeaseNotHonored indicates a worker activated jobs with a lease but the
// server returned a job carrying no lease token.
//
// The specification declares the token present exactly when the activation sets
// the lease flag (see presentwhen.go). A server that predates job leases, or one
// that ignores the flag, breaks that quietly: the worker would go on to complete,
// fail, or throw an error for the job with no token, so the engine could not fence
// the command against a superseded activation. The caller asked for fencing and
// would not be getting it, which is worth failing over rather than logging.
ErrLeaseNotHonored = errors.New("camunda: activation requested a job lease but the server returned no lease token")
)

Sentinel errors. Use errors.Is to test for them.