Contexts with deadlines

May 7, 2025

Today we’re talking about canceling contexts based on time. Arguably, this should have come first, as it’s a little simpler to work with than the explicit cancelation we talked about a week ago. But once again, I’m going in essentially alphabetical order, so it’s what we have…

func WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

WithDeadline returns a derived context that points to the parent context but has the deadline adjusted to be no later than d. If the parent’s deadline is already earlier than d, WithDeadline(parent, d) is semantically equivalent to parent. The returned [Context.Done] channel is closed when the deadline expires, when the returned cancel function is called, or when the parent context’s Done channel is closed, whichever happens first.

Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete.

So here we have the ability to cancel a context at a fixed point in the future.

func cinderella(ctx context.Context) error {
  midnight := time.Now().Add(24 * time.Hour).Truncate(24 * time.Hour)
	ctx, cancel := context.WithDeadline(ctx, midnight)
	defer cancel()

	carriage := pumpkin.Transform()

	go toTheBall(ctx)

	<-ctx.Done() // Wait for context cancellation
	return carriage.Close()
}

What if we have multiple possible deadlines in play on the same context, and want to differentiate between them?

Then you can use the related WithDeadlineCause function:

func WithDeadlineCause

func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc)

WithDeadlineCause behaves like WithDeadline but also sets the cause of the returned Context when the deadline is exceeded. The returned CancelFunc does not set the cause.

This works similarly to the WithCancelCause function recently discussed, but rather than the cancel function accepting the cause, you set the cause at the same time you set the deadline.

ctx, cancel := context.WithDeadlineCause(ctx, midnight, errors.New("magic spell expired"))

Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Context cancelation

As already mentioned, the context.Context type serves two purposes: It manages cancelation signals, and it allows passing request-scoped data. In my opinion, these two purposes are distinct enough that they probably should be handled by two separate mechanisms. But alas, that’s not what we have. However, I think it’s still useful to think of the two uses as distinct. Most often (at least in a well-designed application), the primary purpose of a context is the propegation of cancelation signals.


Contexts with timeouts

func WithTimeout func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete: func slowOperationWithTimeout(ctx context.Context) (Result, error) { ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) defer cancel() // releases resources if slowOperation completes before timeout elapses return slowOperation(ctx) } Not only is WithTimeout conceptually similar to WithDeadline, it literally is WithDeadline, as we can see form the source code:


Context causes

About a week ago, I talked about the two error types that a context may return: context.Canceled and context.DeadlineExceeded. But what if you want to convey some other error? Maybe you want to distinguish between a context that was canceled because the server is shutting down due to a SIGINT, versus becasue the request was canceled by the client, for example? Enter Cause. func Cause func Cause(c Context) error Cause returns a non-nil error explaining why c was canceled.

Get daily content like this in your inbox!

Subscribe