Cancel jobs that have not yet finished successfully.

API

{:ok, cancelled} = Kathikon.cancel(job_id)
cancelled.state        # :cancelled
cancelled.cancelled_at # DateTime.utc_now() at cancel time

Cancellable states

StateCan cancel?
:scheduledYes
:availableYes
:retryableYes
:executingNo{:error, :executing}
:completedNo{:error, {:invalid_state, :completed}}
:cancelledNo{:error, {:invalid_state, :cancelled}}
:discardedNo{:error, {:invalid_state, :discarded}}

Phase 1 does not interrupt in-flight Task processes. A job already running will finish even if you need to cancel it — design workers to check cancellation signals in Phase 4+ or use short tasks.

Example: cancel a scheduled newsletter

{:ok, job} =
  Kathikon.insert(NewsletterWorker, %{"list" => "monthly"},
    schedule_in: 86_400
  )

# User unsubscribes before send
{:ok, cancelled} = Kathikon.cancel(job.id)

Example: double cancel

Kathikon.cancel(job_id)
# {:ok, %Job{state: :cancelled, ...}}

Kathikon.cancel(job_id)
# {:error, {:invalid_state, :cancelled}}

Pruning

Cancelled jobs are terminal. The pruner deletes them after retention_period.

Telemetry

[:kathikon, :job, :cancel] with metadata %{queue: ..., job_id: ...}.