Swift Driver Error Handling Guide
Index
Error Types
The driver uses errors to communicate that an operation failed, an assumption wasn’t met, or that the user did something incorrectly. Applications that use the driver can in turn catch these errors and respond appropriately without crashing or resulting in an otherwise inconsistent state. To correctly model the different sources of errors, the driver defines three separate categories of errors (MongoServerError
, MongoUserError
, MongoRuntimeError
), each of which are protocols that inherit from the MongoErrorProtocol
protocol. These protocols are defined in MongoError.swift
, and the structs that conform to them are outlined here. The documentation for every public function that throws lists some of the errors that could possibly be thrown and the reasons they might be. The errors listed there are not comprehensive but will generally cover the most common cases.
Server Errors
Server errors correspond to failures that occur in the database itself and are returned to the driver via some response to a command. Each error that conforms to ServerError
contains at least one error code representing what went wrong on the server.
For an enumeration of the possible server error codes, see this list.
The possible errors that conform to MongoServerError
are as follows:
MongoError.CommandError
:- Thrown when commands experience errors server side that prevent execution.
- Example command failures include failure to parse, operation aborted by the user, and unexpected errors during execution.
MongoError.WriteError
- Thrown when a single write command fails on the server (e.g. insertOne, updateOne, updateMany).
MongoError.BulkWriteError
- Thrown when the server returns errors as part of an executed bulk write.
- If WriteConcernFailure is populated, writeErrors may not be.
- Note:
InsertMany
throws aMongoError.BulkWriteError
, not aMongoError.WriteError
.
User Errors
User applications can sometimes cause errors by using the driver incorrectly (e.g. by passing invalid argument combinations). This category of error covers those cases.
The possible errors that conform to MongoUserError
are as follows:
MongoError.LogicError
- Thrown when the user uses the driver incorrectly (e.g. advancing a dead cursor).
MongoError.InvalidArgumentError
- Thrown when user passes invalid arguments to some driver function.
Runtime Errors
The driver may experience errors that happen at runtime unexpectedly. These errors don’t fit neatly into the categories of occurring only server-side or only as part of the user’s fault, so they are represented by their own set of cases.
The MongoRuntimeError
cases are as follows:
MongoError.InternalError
- Thrown when something is null when it shouldn’t be, the driver has an internal failure, or MongoSwift cannot understand a server response.
- This is generally indicative of a bug somewhere in the driver stack or a system related failure (e.g. a memory allocation failure). If you experience an error that you think is the result of a bug, please file a bug report on GitHub or our Jira project.
MongoError.ConnectionError
- Thrown during any connection establishment / socket related errors.
- This error also conforms to
LabeledError
.
MongoError.AuthenticationError
- Thrown when the driver is not authorized to perform a requested command (e.g. due to invalid credentials)
MongoError.ServerSelectionError
- Thrown when the driver was unable to select a server for an operation (e.g. due to a timeout or unsatisfiable read preference)
See
See the official MongoDB documentation for more information.
Error Labels
Some types of errors may contain more specific information describing the context in which they occured. Such errors conform to the MongoLabeledError
protocol, and the extra information is conveyed through the errorLabels
property. Specifically, any server error or connection related error may contain labels.
The following error labels are currently defined. Future versions of MongoDB may introduce new labels:
TransientTransactionError
:- Within a multi-document transaction, certain errors can leave the transaction in an unknown or aborted state. These include write conflicts, primary stepdowns, and network errors. In response, the application should abort the transaction and try the same sequence of operations again in a new transaction.
UnknownTransactionCommitResult
:- When
commitTransaction()
encounters a network error or certain server errors, it is not known whether the transaction was committed. Applications should attempt to commit the transaction again until (i) the commit succeeds, (ii) the commit fails with an error not labeledUnknownTransactionCommitResult
, or (iii) the application chooses to give up.
- When
Encoding/Decoding Errors
As part of the driver, BSONEncoder
and BSONDecoder
are implemented according to the Encoder
and Decoder
protocols defined in Apple’s Foundation. User applications can use them to seamlessly convert between their Swift data structures and the BSON documents stored in the database. While this functionality is part of the public API, the driver itself also makes heavy use of it internally. During any encoding or decoding operations, errors can occur that prevent the data from being written to or read from BSON. In these cases, the driver throws an EncodingError
or DecodingError
as appropriate. These error types are not unique to MongoSwift and are commonly used by other encoder implementations, such as Foundation’s JSONEncoder
, so they do not conform to the MongoErrorProtocol
protocol or any of the other error protocols defined in the driver.
See the official documentation for both EncodingErrors
and DecodingErrors
for more information.
BSON Errors
The BSON library has its own subset of errors that communicate issues when constructing or using BSON. BSON Errors can be found in Sources/MongoSwift/BSON/BSONError.swift and are as follows:
BSONError.InvalidArgumentError
- This error is thrown when a BSON type is being incorrectly constructed.BSONError.InternalError
- This error is thrown when there is an issue that is a result of system failure (e.g, allocation issue).BSONError.LogicError
- This error is thrown when there is an unexpected usage of the the API.BSONError.DocumentTooLargeError
- This error is thrown when the document exceeds the maximum encoding size of 16MB.
Examples
Handling any error thrown by the driver
do {
// something involving the driver
} catch let error as MongoErrorProtocol {
print("Driver error!")
switch error.self {
case let runtimeError as MongoRuntimeError:
// handle RuntimeError
case let serverError as MongoServerError:
// handle ServerError
case let userError as MongoUserError:
// handle UserError
default:
// should never get here
}
} catch let error as DecodingError {
// handle DecodingError
} catch let error as EncodingError {
// handle EncodingError
} catch { ... }
Handling a CommandError
do {
try await db.runCommand(["asdfasdf": "sadfsadfasdf"])
} catch let commandError as MongoError.CommandError {
print("Command failed: code: \(commandError.code) message: \(commandError.message)")
} catch { ... }
Output:
Command failed: code: 59 message: no such command: 'asdfasdf'
Handling a WriteError
// if you want to ignore duplicate key errors
do {
try await coll.insertOne(["_id": 1])
try await coll.insertOne(["_id": 1])
} catch let writeError as MongoError.WriteError where writeError.writeFailure?.code == 11000 {
print("duplicate key error: \(1) \(writeError.writeFailure?.message ?? "")")
}
Output:
duplicate key error: 1 E11000 duplicate key error collection: mydb.mycoll1 index: _id_ dup key: { : 1 }
Handling a BulkWriteError
let docs: [BSONDocument] = [["_id": 2], ["_id": 1]]
do {
try await coll.insertOne(["_id": 1])
try await coll.insertMany(docs)
} catch let bwe as MongoError.BulkWriteError {
if let writeErrors = bwe.writeFailures {
writeErrors.forEach { err in print("Write Error inserting \(docs[err.index]), code: \(err.code), message: \(err.message)") }
}
if let result = bwe.result {
print("Result: ")
print("nInserted: \(result.insertedCount)")
print("InsertedIds: \(result.insertedIDs)")
}
} catch { ... }
Output:
Write Error inserting { "_id" : 1 }, code: 11000, message: E11000 duplicate key error collection: mydb.mycoll1 index: _id_ dup key: { : 1 }
Result:
nInserted: 1
InsertedIds: [0: 2]
Handling a BSONError
var string = "+1..23e8"
do {
return try BSONDecimal128(string)
} catch let bsonError as BSONError.InvalidArgumentError {
print(bsonError.message)
}
Output:
Invalid Decimal128 string +1..23e8