Search and navigate

Pages

Start HereRoadmapTutorialsCheatsheetInterview QuestionsResources

Published tutorials

Building a REST API with Go: The Complete TutorialError Handling in Go: Wrapping, Classification, and APIsFan-Out Fan-In in Go: Parallel Work with ChannelsFuzz Testing in Go: Finding Bugs AutomaticallyGeneric Functions in Go: Writing Reusable CodeGo Benchmarking: Measuring Performance with testing.BGo Channel Patterns: Advanced Recipes That WorkGo Channels: Communication Between GoroutinesGo Concurrency Patterns: A Practical CatalogueGo Constraints: comparable, Ordered and Custom SetsGo Context: Cancellation, Deadlines, and Request ValuesGo defer: Cleanup, Ordering, and Common TrapsGo HTTP Client: Timeouts, JSON, and Reliable RequestsGo HTTP Middleware: Build a Production ChainGo Interview Questions: 25 Answers That Show UnderstandingGo Mutex: Protecting Shared State from Race ConditionsGo net/http: Build an HTTP Server from ScratchGo select Statement: Multiplexing Channels ExplainedGo Tools You Need for a Reliable Development LoopGo Tutorial: From First Program to Backend FoundationsGo Type Parameters: Syntax and Constraints ExplainedGo Worker Pool Pattern: Bounded Concurrency at ScaleGo's Type System Deep Dive: Static Typing Done RightGolang Compilation and Execution ExplainedGolang Developer Salary 2026: US, UK and RemoteGolang File Structure for Modules and ApplicationsGolang Hello World and Your First Go ProgramGolang IDEs: Choose an Editor for Your WorkflowGolang Interfaces: What They Are and How to Use ThemGoroutine Leaks in Go: How to Detect and Prevent ThemGoroutines in Go: Concurrency Without LeaksHow to Download and Install Golang SafelyHow to Learn Golang with a Focused Study PlanIntegration Testing in Go: Testing Real BoundariesJSON in Go: Encoding, Decoding, and ValidationLearn Golang with a Practical Beginner CourseMocking in Go: Test Doubles Without Magicsync.WaitGroup in Go: Coordinating Multiple GoroutinesTable-Driven Tests in Go: The Idiomatic WayTesting in Go: Table Tests, HTTP, Fakes, and CoverageWhat Is Go and How Does a Go Program Work?Why Learn Golang and What Is Go Used For?Writing Unit Tests in Go: A Practical Guide
Open full search and filters →

Error Handling in Go: Wrapping, Classification, and APIs

Handle errors idiomatically in Go with wrapping, errors.Is, errors.As, sentinel errors, typed errors, and API mapping.

Standard-library lessonGo requirement: Go 1.21+How tutorials are checked
Fundamentals · Lesson 18Saved in this browser. No account required.
An error moving through application layers while preserving its original cause
Error wrapping adds useful context while preserving the underlying cause for inspection. Image: Golang Tutorial

Go treats errors as values. A function returns an error when its caller can make a meaningful decision about failure. When cleanup must happen on every return path, Kelly’s guide to defer shows how to keep that cleanup beside the resource it protects.

Add context while preserving the cause

user, err := store.Find(ctx, id)
if err != nil {
	return User{}, fmt.Errorf("find user %d: %w", id, err)
}

%w preserves the chain for errors.Is and errors.As. Avoid repeating context already obvious at the next layer.

Classify expected failures

var ErrNotFound = errors.New("not found")

if errors.Is(err, ErrNotFound) {
	// return a 404 or another domain-specific result
}

Sentinel errors work for a small stable category. Use a typed error when callers need structured details.

type ValidationError struct {
	Field string
	Issue string
}

func (e *ValidationError) Error() string {
	return e.Field + ": " + e.Issue
}

Extract it with errors.As instead of a direct type assertion so wrapped errors still match.

Map errors at boundaries

Database errors should not leak into HTTP handlers. A repository maps driver-specific failures to domain errors; the handler maps domain errors to status codes and safe response bodies.

Log once, at the responsible layer

Returning and logging the same error at every layer produces duplicate noise. Add context while returning it, then log at the boundary that decides the operation has failed.

Panic is not ordinary error handling

Use panic for violated invariants or startup conditions the program cannot continue through. Invalid user input, unavailable dependencies, and missing records are ordinary errors.

Good errors answer: what operation failed, which stable category applies, and what the caller can do next, without exposing secrets or internal implementation details.