Swift
error handling
custom message
exception
programming

Simplest way to throw an error/exception with a custom message in Swift?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In Swift, you do not normally throw Java-style exceptions with arbitrary message strings. The idiomatic approach is to define an error type that conforms to Error and attach the message you need to that type. That keeps error handling explicit, type-safe, and compatible with throw, do, try, and catch.

Use a Custom Error Type

The simplest pattern is an enum with an associated string value:

swift
1enum AppError: Error {
2    case custom(String)
3}
4
5func validate(name: String) throws {
6    guard !name.isEmpty else {
7        throw AppError.custom("Name must not be empty")
8    }
9}

This is the Swift equivalent of throwing an error with a custom message. The message is stored inside the case instead of being attached to a generic exception class.

Catch and Read the Message

Once you throw that custom error, you can pattern-match it in a catch block.

swift
1do {
2    try validate(name: "")
3} catch AppError.custom(let message) {
4    print("Validation failed: \(message)")
5} catch {
6    print("Unexpected error: \(error)")
7}

This is one of Swift's strengths: the error payload stays structured instead of forcing everything into untyped string parsing.

A Struct Works Too

If you prefer an error object with a named property, a struct is also fine:

swift
1struct MessageError: Error {
2    let message: String
3}
4
5func loadConfig(path: String) throws {
6    guard path.hasSuffix(".json") else {
7        throw MessageError(message: "Configuration file must be a JSON file")
8    }
9}

This can be a better fit when your error needs multiple fields such as a code, user-facing message, and underlying technical detail.

Use LocalizedError for Better Display Text

If the message is meant for UI or higher-level display, conforming to LocalizedError can make the intent clearer.

swift
1enum LoginError: LocalizedError {
2    case invalidPassword
3    case custom(String)
4
5    var errorDescription: String? {
6        switch self {
7        case .invalidPassword:
8            return "The password is invalid."
9        case .custom(let message):
10            return message
11        }
12    }
13}

Then consumers can read error.localizedDescription:

swift
1do {
2    throw LoginError.custom("Token expired")
3} catch {
4    print(error.localizedDescription)
5}

This is often the cleanest option when you want standard Swift error handling plus a meaningful human-readable description.

Swift Does Not Encourage Exception-Style Flow

The word "exception" often causes confusion because Swift error handling is not modeled after unchecked exception systems. Functions that can throw must be marked throws, and callers must acknowledge that with try.

swift
func riskyOperation() throws {
    throw AppError.custom("Something went wrong")
}

That explicitness is part of the language design. It pushes error cases into the type system instead of making them invisible control flow.

When fatalError Is Different

Sometimes developers really want to stop the program immediately with a message. That is not the same as throwing an error.

swift
fatalError("This code path should never be reached")

fatalError terminates the program. Use it only for programmer mistakes or impossible states, not for recoverable runtime problems such as validation or network failures.

Common Pitfalls

The biggest mistake is looking for a built-in generic exception class that behaves like other languages. Swift expects you to define meaningful error types instead.

Another issue is throwing plain strings. Swift does not allow throw "message" because thrown values must conform to Error.

People also sometimes use fatalError for normal runtime errors. That turns a recoverable problem into an application crash and is usually the wrong tradeoff.

Finally, avoid using a single giant error enum for the whole app if it becomes too vague. Local, domain-specific error types are often easier to understand and test.

Summary

  • In Swift, the idiomatic way to throw a custom message is to throw a custom Error value.
  • An enum with an associated string is often the simplest implementation.
  • A struct or LocalizedError conformance can be better when the error needs richer semantics.
  • 'throw is for recoverable errors; fatalError is for unrecoverable programmer mistakes.'
  • Swift error handling is explicit and typed, not generic exception throwing by message string alone.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions