Unexported context key types

May 27, 2025

Last week we saw the potential dangers of context key collisions. Today let’s look at how to avoid this problem.

The GoDoc we looked at last week gave us a clue:

	// packages should define keys as an unexported type to avoid
	// collisions.

Let’s dive into what this is suggesting, and why it works.

First: context.WithValue accepts any comparable type as a key. This includes custom types. And by using a custom type that’s unexported, we can ensure that no other package uses our keys.

package private

type key string

var myUnexportedKey = key("key")

/* .. later .. */
ctx = context.WithValue(ctx, myUnexportedKey, "value")

Now other packages can create keys all day long with the underlying value of "key", but it will never match your key, because you’ve used your custom type, which is distinct from all others, and no other package can create a value using that type. Woot!


Share this

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

Unsure? Browse the archive .

Related Content


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.


String context keys

By now we’ve looked at empty structs (struct{}), and integers as possible context key types. Today, let’s consider string context keys. type contextKey string const ( KeyUserID contextKey = "user_id" KeyTransactionID contextKey = "transaction_id" KeyMessageID contextKey = "message_id" ) How does this stack up against our 5 criteria? Must be comparable. ✅ Check. Use minimal memory. ✅ Using a string will typically use a bit more memory than an integer (typically 32 bytes vs 16), but still quite minimal.

Get daily content like this in your inbox!

Subscribe