Bird
Raised Fist0
Goprogramming~15 mins

Returning errors in Go - Deep Dive

Choose your learning style10 modes available
Overview - Returning errors
What is it?
Returning errors in Go means that functions can send back information about problems they encountered while running. Instead of stopping the program immediately, they give a special value called an error. This lets the part of the program that called the function decide what to do next. It’s like a function saying, "I tried, but something went wrong."
Why it matters
Without returning errors, programs would crash or behave unpredictably when something unexpected happens. Returning errors lets programs handle problems gracefully, like asking for help or trying a different way. This makes software more reliable and easier to fix when bugs appear.
Where it fits
Before learning to return errors, you should understand basic Go functions and types. After this, you can learn about error handling patterns, custom error types, and how to use Go’s built-in error tools like errors.Is and errors.As.
Mental Model
Core Idea
Returning errors is like a function handing back a note that says whether it succeeded or what problem it found, so the caller can decide what to do next.
Think of it like...
Imagine you ask a friend to pick up groceries. Instead of just giving you the groceries, they also tell you if the store was closed or if they forgot something. That way, you know what happened and can decide if you want to try again or do something else.
┌───────────────┐       ┌───────────────┐
│   Caller      │──────▶│   Function    │
│               │       │               │
│ Calls function│       │ Does work     │
│               │       │ Returns result│
│               │◀──────│ and error     │
└───────────────┘       └───────────────┘
Build-Up - 7 Steps
1
FoundationBasic function return values
🤔
Concept: Functions can return values to the caller.
In Go, functions can send back one or more values. For example, a function can return a number or a word after doing some work. func add(a int, b int) int { return a + b } result := add(2, 3) // result is 5
Result
The caller gets the sum 5 from the add function.
Understanding that functions can send back values is the first step to knowing how they can also send back errors.
2
FoundationIntroducing the error type
🤔
Concept: Go has a special type called error to represent problems.
The error type in Go is a built-in interface. It holds a message describing what went wrong. var err error = errors.New("something went wrong") This error can be returned by functions to signal failure.
Result
You have a way to represent errors as values in Go.
Knowing that errors are just values lets you treat problems like normal data, making error handling flexible.
3
IntermediateReturning errors from functions
🤔Before reading on: do you think a function can return both a result and an error at the same time? Commit to your answer.
Concept: Functions can return multiple values, including an error to show success or failure.
In Go, functions often return two values: the main result and an error. If the error is nil, the result is valid. func divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("cannot divide by zero") } return a / b, nil } result, err := divide(10, 0) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("Result:", result) }
Result
The program prints: Error: cannot divide by zero
Understanding that errors are returned alongside results lets you write functions that clearly communicate success or failure.
4
IntermediateChecking and handling errors
🤔Before reading on: do you think ignoring errors is safe in Go programs? Commit to your answer.
Concept: After calling a function that returns an error, you should check the error before using the result.
When you get an error from a function, you must check if it is nil. If it’s not nil, handle the problem before continuing. value, err := someFunction() if err != nil { // handle error, maybe return or log return } // safe to use value here
Result
The program avoids crashes or wrong results by handling errors properly.
Knowing to always check errors prevents bugs and crashes caused by unexpected problems.
5
IntermediateUsing errors.New and fmt.Errorf
🤔Before reading on: do you think error messages can include dynamic information like numbers or strings? Commit to your answer.
Concept: You can create error messages with fixed text or formatted strings that include details.
errors.New creates a simple error with a fixed message. fmt.Errorf lets you build error messages with variables. err := errors.New("file not found") err2 := fmt.Errorf("file %s not found", filename)
Result
Error messages can be clear and include useful details for debugging.
Knowing how to create informative error messages helps users and developers understand what went wrong.
6
AdvancedCustom error types for richer info
🤔Before reading on: do you think errors can carry more than just a message? Commit to your answer.
Concept: You can define your own error types with extra data and behavior.
By creating a struct that implements the Error() string method, you can add fields like codes or context. type MyError struct { Code int Msg string } func (e MyError) Error() string { return fmt.Sprintf("Error %d: %s", e.Code, e.Msg) } func doSomething() error { return MyError{Code: 404, Msg: "Not found"} }
Result
Errors can carry structured information, making handling and logging more powerful.
Understanding custom errors unlocks advanced error handling strategies in real applications.
7
ExpertError wrapping and unwrapping
🤔Before reading on: do you think you can keep the original error inside a new error to add context? Commit to your answer.
Concept: Go lets you wrap errors to add context while preserving the original error for inspection.
Using fmt.Errorf with %w verb, you can wrap an error: origErr := errors.New("disk full") wrappedErr := fmt.Errorf("failed to save file: %w", origErr) Later, you can check if an error wraps another using errors.Is or errors.As. if errors.Is(wrappedErr, origErr) { fmt.Println("Original error found") }
Result
You can add helpful context to errors without losing the original cause.
Knowing error wrapping helps build clear error chains that are easier to debug and handle precisely.
Under the Hood
In Go, errors are values that implement the error interface, which requires a single Error() string method. When a function returns an error, it is just returning a value like any other. The caller receives this value and can inspect or pass it along. Wrapping errors creates new error values that hold references to the original error, enabling unwrapping. This design uses Go’s interface and value semantics to keep error handling simple and flexible.
Why designed this way?
Go was designed to keep error handling explicit and clear, avoiding exceptions or hidden control flow. Returning errors as values fits Go’s philosophy of simplicity and transparency. Wrapping errors was added later to improve context without breaking existing code. Alternatives like exceptions were rejected because they hide errors and make code harder to follow.
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│   Caller      │──────▶│   Function    │──────▶│   Returns     │
│               │       │               │       │  (result, err)│
│ Checks error  │◀──────│               │◀──────│ error value   │
│ Handles error │       │               │       │               │
└───────────────┘       └───────────────┘       └───────────────┘
Myth Busters - 3 Common Misconceptions
Quick: Do you think ignoring the error return in Go is safe and common? Commit to yes or no before reading on.
Common Belief:Many believe that if the error is not important, you can just ignore it and use the result directly.
Tap to reveal reality
Reality:Ignoring errors can cause hidden bugs, crashes, or incorrect results because the function might have failed silently.
Why it matters:Ignoring errors leads to unstable programs that are hard to debug and can cause data loss or security issues.
Quick: Do you think errors in Go are always just plain text messages? Commit to yes or no before reading on.
Common Belief:People often think errors only carry simple text messages describing the problem.
Tap to reveal reality
Reality:Errors can be custom types carrying codes, context, or even methods to handle them differently.
Why it matters:Treating errors as just text limits your ability to handle different error cases properly in complex programs.
Quick: Do you think wrapping errors loses the original error information? Commit to yes or no before reading on.
Common Belief:Some believe that wrapping an error replaces the original error and loses its details.
Tap to reveal reality
Reality:Go’s error wrapping preserves the original error inside the new one, allowing inspection and unwrapping.
Why it matters:Misunderstanding wrapping can cause developers to miss the root cause of errors during debugging.
Expert Zone
1
Custom error types can implement additional interfaces like Temporary or Timeout to signal retry behavior.
2
Error wrapping should be done carefully to avoid creating long chains that are hard to read or cause performance issues.
3
Using errors.Is and errors.As properly allows precise error handling without brittle string comparisons.
When NOT to use
Returning errors is not suitable for asynchronous or concurrent error reporting where channels or other signaling methods are better. Also, for very simple scripts, panics might be acceptable for unrecoverable errors instead of returning errors everywhere.
Production Patterns
In production Go code, errors are often wrapped with context at each layer, logged with structured logging, and checked explicitly. Libraries provide sentinel errors for common cases, and custom error types help differentiate error handling paths. Using errors.Is and errors.As is standard for matching error types.
Connections
Exception handling (other languages)
Alternative error handling approach
Understanding Go’s explicit error returns helps appreciate the tradeoffs compared to exceptions, which hide control flow and can cause unexpected crashes.
Functional programming Maybe/Option types
Similar pattern of explicit error or absence handling
Both Go errors and Maybe types force the programmer to handle failure cases explicitly, improving code safety and clarity.
Customer service feedback loops
Error reporting as communication
Just like returning errors is a function communicating problems to its caller, customer feedback loops let businesses learn and improve by reporting issues clearly.
Common Pitfalls
#1Ignoring the error return and using the result blindly
Wrong approach:result, _ := divide(10, 0) fmt.Println("Result:", result)
Correct approach:result, err := divide(10, 0) if err != nil { fmt.Println("Error:", err) return } fmt.Println("Result:", result)
Root cause:Beginners often think the underscore means the error is unimportant, but ignoring errors hides failures.
#2Creating error messages without context or details
Wrong approach:return errors.New("failed")
Correct approach:return fmt.Errorf("failed to open file %s", filename)
Root cause:Not including details makes debugging harder because the error message is vague.
#3Wrapping errors without using %w verb
Wrong approach:return fmt.Errorf("operation failed: %v", err)
Correct approach:return fmt.Errorf("operation failed: %w", err)
Root cause:Using %v instead of %w loses the ability to unwrap and inspect the original error.
Key Takeaways
Returning errors in Go is a way for functions to communicate problems without stopping the program abruptly.
Errors are values that must be checked explicitly to keep programs safe and predictable.
You can create simple or custom error types to provide more information about failures.
Wrapping errors adds context while preserving the original cause, making debugging easier.
Ignoring errors or mishandling them leads to bugs, crashes, and hard-to-maintain code.

Practice

(1/5)
1.

What does a Go function return when there is no error?

easy
A. It returns nil as the error value.
B. It returns 0 as the error value.
C. It returns "no error" as the error value.
D. It returns an empty string "" as the error value.

Solution

  1. Step 1: Understand Go error convention

    In Go, functions return an error type to indicate problems. If no problem occurs, the error is nil.
  2. Step 2: Identify the correct no-error return value

    The standard way to show no error is to return nil, not zero, empty string, or text.
  3. Final Answer:

    It returns nil as the error value. -> Option A
  4. Quick Check:

    No error means error = nil [OK]
Hint: No error always returns nil, never zero or empty string [OK]
Common Mistakes:
  • Returning 0 instead of nil for no error
  • Returning empty string instead of nil
  • Returning a string message instead of nil
2.

Which of the following is the correct way to declare a Go function that returns an int and an error?

func example() ???
easy
A. func example() (int, error)
B. func example() int, error
C. func example() int error
D. func example() (int error)

Solution

  1. Step 1: Recall Go function return syntax

    Go functions return multiple values by listing types in parentheses separated by commas.
  2. Step 2: Match correct syntax

    The correct syntax for returning an int and error is (int, error). Other options miss commas or parentheses.
  3. Final Answer:

    func example() (int, error) -> Option A
  4. Quick Check:

    Multiple returns use parentheses and commas [OK]
Hint: Use parentheses and commas for multiple return types [OK]
Common Mistakes:
  • Omitting parentheses for multiple returns
  • Missing comma between return types
  • Writing return types without parentheses
3.

What will be the output of this Go program?

package main
import (
  "errors"
  "fmt"
)
func divide(a, b int) (int, error) {
  if b == 0 {
    return 0, errors.New("division by zero")
  }
  return a / b, nil
}
func main() {
  result, err := divide(10, 0)
  if err != nil {
    fmt.Println(err)
  } else {
    fmt.Println(result)
  }
}
medium
A. runtime error
B. division by zero
C. 10
D. 0

Solution

  1. Step 1: Analyze divide function behavior

    If divisor b is zero, function returns error with message "division by zero" and result 0.
  2. Step 2: Check main function output

    Main calls divide(10, 0), gets error != nil, so prints the error message.
  3. Final Answer:

    division by zero -> Option B
  4. Quick Check:

    Error returned prints message [OK]
Hint: If error is not nil, print error message [OK]
Common Mistakes:
  • Printing result even when error is not nil
  • Expecting 0 as output instead of error message
  • Assuming runtime panic on division by zero
4.

Identify the error in this Go function that returns an error:

func readFile(name string) (string, error) {
  data := ""
  file, err := os.Open(name)
  if err != nil {
    return data, err
  }
  defer file.Close()
  // read file content
  return data, nil
}
medium
A. Function should return only error, not string.
B. Return statement should return only data.
C. Defer statement should be before error check.
D. Missing import of os package.

Solution

  1. Step 1: Check for required imports

    The function uses os.Open but does not import os package, causing a compile error.
  2. Step 2: Verify other code parts

    Return types and defer placement are correct; only missing import is the problem.
  3. Final Answer:

    Missing import of os package. -> Option D
  4. Quick Check:

    Using os.Open requires importing os [OK]
Hint: Check imports when using standard library functions [OK]
Common Mistakes:
  • Forgetting to import packages used
  • Placing defer before error check
  • Returning wrong number of values
5.

You want to write a Go function parseNumber that takes a string and returns an int and an error. It should return an error if the string is empty or not a valid number. Which implementation correctly returns errors?

func parseNumber(s string) (int, error) {
  if s == "" {
    return 0, errors.New("empty input")
  }
  n, err := strconv.Atoi(s)
  if err != nil {
    return 0, err
  }
  return n, nil
}
hard
A. Returns error only for invalid number, not empty string.
B. Returns nil error even if input is empty.
C. Correctly returns errors for empty and invalid input.
D. Returns zero and nil error for all inputs.

Solution

  1. Step 1: Check empty string error handling

    The function returns error "empty input" if string is empty, which is correct.
  2. Step 2: Check invalid number error handling

    Uses strconv.Atoi to convert string; returns error if conversion fails.
  3. Step 3: Confirm success return

    If no error, returns parsed number and nil error.
  4. Final Answer:

    Correctly returns errors for empty and invalid input. -> Option C
  5. Quick Check:

    Empty and invalid inputs return errors [OK]
Hint: Return error immediately on invalid input [OK]
Common Mistakes:
  • Not checking empty string before conversion
  • Ignoring error from strconv.Atoi
  • Returning nil error on invalid input
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard