Context value key collisions

May 23, 2025

Let’s talk about keys for context values.

type Context

type Context interface {
…
	// A key identifies a specific value in a Context. Functions that wish
	// to store values in Context typically allocate a key in a global
	// variable then use that key as the argument to context.WithValue and
	// Context.Value. A key can be any type that supports equality;
	// packages should define keys as an unexported type to avoid
	// collisions.

There’s a lot to unpack in this small, seemingly innocuous paragraph. Probably two or three full-length blog posts worth, if we try. But for today, let’s explain what it means by “collisions”.

Let’s imagine your code base has a package for the database, we’ll call it package db, and one for authentication, package auth.

Both packages have a sense of a user, that is request-scoped. In the auth package, the user represents the user that has logged into the HTTP service. In the db package, it represents a database role, for data-layer access control. So two different concepts of a user.

To accomplish this, we have:

package auth

/* ... */
authUser := LookupAuthUser(/* .. */)
ctx = context.WithValue(ctx, "user", authUser)

as well as:

package db

dbUser := LookupDBUser(/* .. */)
ctx = context.WithValue(ctx, "user", dbUser)

Can you see where we’re going with this?

These two packages are likely to interact with each other in certain ways. For example the auth package may call a function in the db package to look up the user’s password for validation purposes… And when that happens, the db package may do something like this:

user, ok := ctx.Value("user").(*DBUser)
if !ok {
	return errors.New("db user not found")
}

If the "user" key exists, but was set by the auth package, that type assertion will fail due to a context key collision—two different packages attempting to use the same key for different purposes.

Next we’ll be looking at techniques to avoid this problem.


Share this

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

Unsure? Browse the archive .

Related Content


Context key/value utilities

So we’ve finally settled on a suitable type for our context key types: string, or maybe a non-empty struct. We also understand that to guard against key collisions, we need our custom type to be un-exported. type contextKey string const KeyUserID = contextKey("user_id") Now code anywhere in your project can use the context key without fear of collision. Done! Right? I’m sure you know the fact that I’m asking means there’s more we can do.


Context key type: Final recommendation

First, a correction! An astute reader pointed out that I made a small mistake in my post on June, 10, with regard to string context keys. My code example showed: type contextKey string const ( KeyUserID = "user_id" KeyTransactionID = "transaction_id" KeyMessageID = "message_id" ) But it should have been: type contextKey string const ( KeyUserID contextKey = "user_id" KeyTransactionID contextKey = "transaction_id" KeyMessageID contextKey = "message_id" ) This is a nasty kind of bug, because the code will continue to work as expected—just without any protection from key collisions!


Struct context keys

First off, an apology for being rather unreliable with my “daily” emails. I’m in the middle of some traveling (was in the UK and Netherlands last week, and will be on a US road trip starting tomorrow), so I’m writing when I have a spare moment, which isn’t that often. I’ve been talking about finding the ideal type for a context key. So far I’ve looked at empty structs (e.g. struct{}), integers, and strings.

Get daily content like this in your inbox!

Subscribe