go / must

I use small must helper functions to convert errors into panics in code paths where errors indicate programmer mistakes: startup, test setup, and internal invariants that should never fail.

The pattern

A 4-line function, defined inline in whichever package needs it:

func must(err error) {
	if err != nil {
		panic(err)
	}
}

Used at startup or in cleanup paths where there's no sensible fallback:

must(os.Setenv("GIT_CONFIG_GLOBAL", gitConfigGlobal))
must(os.RemoveAll(rootDir))
must(os.MkdirAll(wsDir, 0700))
must(command(ctx, os.Stdout, "git", "clone", repoURL, repoDir).Run())

If any of these fail, the process can't make progress. Better to crash with a stack trace than to limp along.

Test variant

In tests, take *testing.T so the failure stops the test gracefully and the message points at the call site, not the helper:

func must(t *testing.T, err error) {
	t.Helper()
	if err != nil {
		t.Fatal(err)
	}
}

t.Helper() makes the failure report the test function's line, not the line inside must. Same idea as the log helper registry, applied to test output.

Usage:

func TestSchema(t *testing.T) {
	db = pqtest.Open(t, pqtest.SchemaFile("schema.sql"))
	defer db.Close()

	must(t, boxPing(ctx, BoxPingReq{ID: "box1"}))
	must(t, upsertJobs(ctx, "commit1", "/", []string{"cmd1"}))
	must(t, markDone(ctx, job, "error", "...", "", 0))
}

Inline or package?

I keep must inline in whichever package needs it. The function is small enough that copy-paste costs less than threading an import through a project, and each variant (production vs. test) has its own signature anyway.

For widely-needed cases the standard library already provides Must* wrappers:

Reach for those first.

Returning a value

If you do need a generic version that returns a value (e.g. for var x = mustGet(somefunc()) at package level), keep it inline too:

func mustGet[V any](v V, err error) V {
	if err != nil {
		panic(err)
	}
	return v
}

var configRe = mustGet(regexp.Compile(`^[a-z]+$`))

In practice the stdlib Must* variants cover this case for regex, templates, and a few others, so I rarely need it.

When to use

When not to use

← All articles