Skip to main content

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

PropertyTypeRequiredDescription
JobIdstringYesUnique identifier assigned by the server.
StateJobStateYesCurrent lifecycle state of the job.
JobTypestringYesFully-qualified CLR type name (e.g. "MyApp.Jobs.SendEmail").
QueuestringYesThe queue this job is assigned to.
CreatedAtDateTimeOffsetYesWhen the job was created.
StartedAtDateTimeOffset?NoWhen a worker started processing. null until Processing.
CompletedAtDateTimeOffset?NoWhen the job reached a terminal state. null while in progress.
AttemptNumberintNoCurrent attempt number (starts at 1).
MaxAttemptsintNoMaximum attempts configured for this job.
Progressdouble?NoExecution progress (0.0 to 1.0), if reported by the job.
ErrorMessagestring?NoError message from the most recent failed attempt.
TagsDictionary<string, string>?NoCustom metadata set when the job was enqueued.

Timing properties

The timing properties reflect the job's lifecycle:

StateStartedAtCompletedAt
Pendingnullnull
Schedulednullnull
ProcessingSetnull
SucceededSetSet
FailedSetSet
CancellednullSet
DeadLetterSetSet

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
});
});
tip

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).

tip

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
  • IJobClientGetStatusAsync returns this object
  • JobOptionsTags set at enqueue time appear in JobStatus.Tags