Look, I’m gonna be straight with you. Go’s error handling is fucking verbose, and debugging without context is like trying to find your keys in a pitch-black room while drunk. You know the drill — you get some generic error message and spend the next 2 hours playing detective, hunting through logs like some sad CSI wannabe.
So I built ctxerrors because I was tired of this bullshit.
The Problem That Pissed Me Off
Picture this: you’re deep in some gnarly Go codebase, everything’s going fine, and then BAM — error. But not just any error, oh no. You get something useful like:
connection refused
That’s it. That’s all you get. WHERE did it fail? WHICH function? WHAT line? Fucking mystery, mate.
You’re left playing 20 questions with your own code:
- Was it the database connection?
- The HTTP client?
- That sketchy third-party API call?
- Your own shitty code from 3 months ago that you wrote at 2 AM?
The Solution (That Actually Works)
So I wrote ctxerrors
— a dead simple Go package that automatically captures where your errors happen. Every single error gets tagged with:
- File name — so you know exactly which file
- Line number — pinpoint precision, none of this guessing bullshit
- Function name — because context matters
Here’s how it works:
Basic Usage (For When You Want to Stay Sane)
Instead of:
return errors.New("something broke")
You write:
return ctxerrors.New("something broke")
And instead of getting a useless error message, you get:
something broke [/path/to/your/file.go:42 in main.doStuff]
Boom. No more detective work.
Error Wrapping (The Good Shit)
But wait, it gets better. You can wrap existing errors and build a full trace:
func readConfig() error {
return ctxerrors.New("config file missing")
}
func initDatabase() error {
if err := readConfig(); err != nil {
return ctxerrors.Wrap(err, "failed to read config")
}
return nil
}
func startServer() error {
if err := initDatabase(); err != nil {
return ctxerrors.Wrap(err, "database initialization failed")
}
return nil
}
When this chain of fuckups happens, you get a beautiful error trace:
database initialization failed: failed to read config: config file missing [/path/main.go:12 in main.readConfig] [/path/main.go:17 in main.initDatabase] [/path/main.go:24 in main.startServer]
No more mystery. No more guesswork. Just cold, hard facts about where everything went tits up.
Why This Matters
Look, I get it. There are other error handling libraries out there. But most of them are either:
- Over-engineered pieces of shit that try to do everything and end up doing nothing well
- Academic wankery that looks good in blog posts but sucks in real code
- Enterprise bullshit that requires 47 configuration files and a PhD to use
ctxerrors
does exactly one thing: it tells you where your code failed. That’s it. No bells, no whistles, no corporate buzzword bullshit.
Real-World Example (The Stuff That Actually Happens)
Here’s some actual code from a project I’m working on:
func processUserData(userID int) error {
// Validate the user exists
if err := validateUser(userID); err != nil {
return ctxerrors.Wrap(err, "user validation failed")
}
// Fetch their profile
profile, err := fetchUserProfile(userID)
if err != nil {
return ctxerrors.Wrapf(err, "failed to fetch profile for user %d", userID)
}
// Update their settings
if err := updateUserSettings(profile); err != nil {
return ctxerrors.Wrap(err, "settings update failed")
}
return nil
}
When this fails (and it will, because everything fails), I don’t have to dig through logs or add temporary debug statements. I get a clean trace showing exactly what happened and where.
Installation & Usage
Dead simple:
go get github.com/psyb0t/ctxerrors
Then just replace your errors.New()
calls with ctxerrors.New()
and your fmt.Errorf()
wrapping with ctxerrors.Wrap()
or ctxerrors.Wrapf()
.
The package plays nice with Go’s standard error handling — errors.Unwrap()
, errors.Is()
, and errors.As()
all work exactly as you’d expect.
The Bottom Line
Debugging is already painful enough without having to play guessing games with error locations. ctxerrors
gives you the context you need to fix shit fast and get on with your life.
Is it revolutionary? Nah. Is it useful? Absolutely. Will it save you time and frustration? You bet your ass it will.
Bonus: For the Masochists
If you really want to piss off your future self (or your coworkers), you can write absolutely ridiculous nested anonymous function chains like this:
func performStupidlyComplexOperation() error {
// Because apparently we hate ourselves and everyone who reads this code
return func() error {
// Welcome to nested function hell, population: you
if err := func() error {
// This is where sanity comes to die
if err := func() error {
// At this point we're just fucking with people
if err := func() error {
// The beginning of the end
if err := func() error {
// Rock bottom of this shitshow
return ctxerrors.New("step 1 went to shit")
}(); err != nil {
// Step 2: electric boogaloo of failure
return ctxerrors.Wrap(err, "step 2 couldn't handle step 1's bullshit")
}
return nil
}(); err != nil {
// Step 3: the reckoning
return ctxerrors.Wrap(err, "step 3 is having a mental breakdown")
}
return nil
}(); err != nil {
// Step 4: fuck it, we're done trying
return ctxerrors.Wrap(err, "step 4 said fuck this shit")
}
return nil
}(); err != nil {
// The final boss of this clusterfuck
return ctxerrors.Wrap(err, "the entire fucking operation is fucked")
}
return nil
}() // Because we needed one more layer of stupid
}
When this abomination fails, you still get perfect tracing:
the entire fucking operation is fucked: step 4 said fuck this shit: step 3 is having a mental breakdown: step 2 couldn't handle step 1's bullshit: step 1 went to shit [main.go:42 in step1] [main.go:47 in step2] [main.go:53 in step3] [main.go:59 in step4] [main.go:65 in performStupidlyComplexOperation]
Even when your code looks like it was written by someone having a psychotic break, ctxerrors
still has your back. That’s the beauty of it.
The Bottom Line
Go check it out: github.com/psyb0t/ctxerrors
And if you find it useful, give it a star or whatever. Or don’t. I’m not your boss.
P.S. — If you’re one of those people who thinks proper error handling is “enterprise bloat,” this package probably isn’t for you. Go back to your fmt.Println()
debugging and leave the adults alone.