JobStatus
A read-only snapshot of a background job's current status. Returned by IJobClient.GetStatusAsync.
Namespace: Zeridion.Flare · Assembly: Zeridion.Flare.dll
public sealed class JobStatus
{
public required string JobId { get; init; }
public required JobState State { get; init; }
public required string JobType { get; init; }
public required string Queue { get; init; }
public required DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset? StartedAt { get; init; }
public DateTimeOffset? CompletedAt { get; init; }
public int AttemptNumber { get; init; }
public int MaxAttempts { get; init; }
public double? Progress { get; init; }
public string? ErrorMessage { get; init; }
public Dictionary<string, string>? Tags { get; init; }
}
Properties
| Property | Type | Required | Description |
|---|---|---|---|
JobId | string | Yes | Unique identifier assigned by the server. |
State | JobState | Yes | Current lifecycle state of the job. |
JobType | string | Yes | Fully-qualified CLR type name (e.g. "MyApp.Jobs.SendEmail"). |
Queue | string | Yes | The queue this job is assigned to. |
CreatedAt | DateTimeOffset | Yes | When the job was created. |
StartedAt | DateTimeOffset? | No | When a worker started processing. null until Processing. |
CompletedAt | DateTimeOffset? | No | When the job reached a terminal state. null while in progress. |
AttemptNumber | int | No | Current attempt number (starts at 1). |
MaxAttempts | int | No | Maximum attempts configured for this job. |
Progress | double? | No | Execution progress (0.0 to 1.0), if reported by the job. |
ErrorMessage | string? | No | Error message from the most recent failed attempt. |
Tags | Dictionary<string, string>? | No | Custom metadata set when the job was enqueued. |
Timing properties
The timing properties reflect the job's lifecycle:
| State | StartedAt | CompletedAt |
|---|---|---|
Pending | null | null |
Scheduled | null | null |
Processing | Set | null |
Succeeded | Set | Set |
Failed | Set | Set |
Cancelled | null | Set |
DeadLetter | Set | Set |
Checking job state
Use pattern matching or switch expressions to branch on the job's state:
var status = await jobs.GetStatusAsync(jobId);
if (status is null)
{
Console.WriteLine("Job not found");
return;
}
var message = status.State switch
{
JobState.Pending => "Waiting in queue",
JobState.Scheduled => "Scheduled for later",
JobState.Processing => $"Running (attempt {status.AttemptNumber}/{status.MaxAttempts})",
JobState.Succeeded => $"Completed in {(status.CompletedAt!.Value - status.StartedAt!.Value).TotalSeconds:F1}s",
JobState.Failed => $"Failed: {status.ErrorMessage}",
JobState.Cancelled => "Cancelled",
JobState.DeadLetter => $"Dead letter after {status.MaxAttempts} attempts: {status.ErrorMessage}",
_ => "Unknown state"
};
Polling for completion
For request-reply patterns where you need to wait for a job to finish:
app.MapGet("/orders/{id}/status", async (string id, IJobClient jobs) =>
{
var status = await jobs.GetStatusAsync(id);
if (status is null)
return Results.NotFound();
return Results.Ok(new
{
job_id = status.JobId,
state = status.State,
progress = status.Progress,
error = status.ErrorMessage,
completed_at = status.CompletedAt
});
});
When serializing JobState to JSON, use System.Text.Json with JsonStringEnumConverter and JsonNamingPolicy.SnakeCaseLower to produce API-compatible state strings like "dead_letter". Avoid ToString().ToLowerInvariant() which produces "deadletter" (no underscore).
Avoid tight polling loops. Use a reasonable interval (1-5 seconds) and set an upper bound on the number of checks. For real-time updates, consider checking the dashboard instead.
Usage example
A diagnostic endpoint that returns rich job information:
app.MapGet("/jobs/{id}/details", async (string id, IJobClient jobs) =>
{
var status = await jobs.GetStatusAsync(id);
if (status is null)
return Results.NotFound(new { error = "Job not found" });
return Results.Ok(new
{
job_id = status.JobId,
type = status.JobType,
queue = status.Queue,
state = status.State,
attempt = $"{status.AttemptNumber}/{status.MaxAttempts}",
created_at = status.CreatedAt,
started_at = status.StartedAt,
completed_at = status.CompletedAt,
duration = status.StartedAt.HasValue && status.CompletedAt.HasValue
? (status.CompletedAt.Value - status.StartedAt.Value).TotalMilliseconds
: (double?)null,
progress = status.Progress,
error = status.ErrorMessage,
tags = status.Tags
});
});
See also
- JobState — the lifecycle states and state machine diagram
- IJobClient —
GetStatusAsyncreturns this object - JobOptions —
Tagsset at enqueue time appear inJobStatus.Tags