• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / supabase-swift / 25175591666

30 Apr 2026 03:57PM UTC coverage: 76.107% (-4.8%) from 80.858%
25175591666

Pull #987

github

web-flow
Merge 3cde54759 into 9e0637908
Pull Request #987: feat(storage)!: storage v3

578 of 1066 new or added lines in 7 files covered. (54.22%)

11 existing lines in 2 files now uncovered.

6756 of 8877 relevant lines covered (76.11%)

29.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

12.0
/Sources/Storage/StorageError.swift
1
//
2
//  StorageError.swift
3
//  Storage
4
//
5
//  Created by Guilherme Souza on 22/05/24.
6
//
7

8
import Foundation
9

10
#if canImport(FoundationNetworking)
11
  import FoundationNetworking
12
#endif
13

14
/// A typed code identifying the specific error returned by the Storage server.
15
///
16
/// Known server error strings are exposed as static constants. Because the server may return codes
17
/// not listed here (e.g. when the SDK is older than the server), the type is open-ended: any
18
/// unrecognised string is representable without breaking existing `switch` statements.
19
///
20
/// ## Example
21
///
22
/// ```swift
23
/// catch let error as StorageError {
24
///   if error.errorCode == .objectNotFound { /* handle missing object */ }
25
/// }
26
/// ```
27
// Intentionally not Decodable — server JSON decoding is handled by the private
28
// ServerErrorResponse struct in StorageClient, which constructs StorageErrorCode values directly.
29
public struct StorageErrorCode: RawRepresentable, Sendable, Hashable {
30
  public var rawValue: String
31

NEW
32
  public init(rawValue: String) {
×
NEW
33
    self.rawValue = rawValue
×
NEW
34
  }
×
35

NEW
36
  public init(_ rawValue: String) {
×
NEW
37
    self.init(rawValue: rawValue)
×
NEW
38
  }
×
39
}
40

41
extension StorageErrorCode {
42
  /// Fallback used when the server returns an unrecognised code or a non-JSON body.
43
  public static let unknown = StorageErrorCode("unknown")
44

45
  // MARK: - Authentication / authorisation
46

47
  /// No API key was supplied with the request.
48
  public static let noApiKey = StorageErrorCode("NoApiKeyFound")
49
  /// The JWT supplied with the request is invalid.
50
  public static let invalidJWT = StorageErrorCode("InvalidJWT")
51
  /// The request was rejected because the caller is not authorised.
52
  public static let unauthorized = StorageErrorCode("Unauthorized")
53

54
  // MARK: - Object / bucket
55

56
  /// Generic not-found response (no further specificity from the server).
57
  public static let notFound = StorageErrorCode("NotFound")
58
  /// The requested object does not exist.
59
  public static let objectNotFound = StorageErrorCode("ObjectNotFound")
60
  /// The requested bucket does not exist.
61
  public static let bucketNotFound = StorageErrorCode("BucketNotFound")
62
  /// An object at the given path already exists and upsert was not requested.
63
  public static let objectAlreadyExists = StorageErrorCode("Duplicate")
64
  /// A bucket with the given name already exists.
65
  public static let bucketAlreadyExists = StorageErrorCode("BucketAlreadyExists")
66
  /// The bucket name does not meet naming requirements.
67
  public static let invalidBucketName = StorageErrorCode("InvalidBucketName")
68

69
  // MARK: - Upload
70

71
  /// The uploaded file exceeds the configured size limit.
72
  public static let entityTooLarge = StorageErrorCode("EntityTooLarge")
73
  /// The MIME type of the uploaded file is not allowed.
74
  public static let invalidMimeType = StorageErrorCode("InvalidMimeType")
75
  /// The request did not include a Content-Type header.
76
  public static let missingContentType = StorageErrorCode("MissingContentType")
77

78
  // MARK: - Client-side synthetic codes (no HTTP response)
79

80
  /// The signed upload URL returned by the server contained no upload token.
81
  public static let noTokenReturned = StorageErrorCode("noTokenReturned")
82
}
83

84
/// An error thrown by the Supabase Storage API.
85
///
86
/// All Storage operations throw ``StorageError`` on failure. Use ``message`` for a human-readable
87
/// description, ``errorCode`` to identify the specific failure kind, and ``statusCode`` for the
88
/// HTTP status when the error originated from a server response.
89
///
90
/// Adding new ``StorageErrorCode`` constants in future SDK versions is not a breaking change.
91
///
92
/// ## Example
93
///
94
/// ```swift
95
/// do {
96
///   try await storage.from("avatars").upload("image.png", data: data)
97
/// } catch let error as StorageError {
98
///   switch error.errorCode {
99
///   case .objectAlreadyExists:
100
///     print("File already exists — use upsert: true to overwrite")
101
///   case .entityTooLarge:
102
///     print("File is too large")
103
///   default:
104
///     print("Storage error \(error.statusCode ?? -1): \(error.message)")
105
///   }
106
/// }
107
/// ```
108
public struct StorageError: Error, Sendable {
109
  /// A human-readable description of what went wrong.
110
  public let message: String
111

112
  /// A typed error code identifying the specific failure.
113
  ///
114
  /// Set to ``StorageErrorCode/unknown`` when the server returns an unrecognised code or a
115
  /// non-JSON response body.
116
  public let errorCode: StorageErrorCode
117

118
  /// The HTTP status code returned by the server.
119
  ///
120
  /// `nil` for client-side errors that have no associated HTTP response
121
  /// (e.g. ``StorageError/noTokenReturned``).
122
  public let statusCode: Int?
123

124
  /// The raw HTTP response, available for advanced debugging.
125
  ///
126
  /// `nil` for client-side errors.
127
  public let underlyingResponse: HTTPURLResponse?
128

129
  /// The raw response body, available for advanced debugging.
130
  ///
131
  /// `nil` for client-side errors.
132
  public let underlyingData: Data?
133

134
  /// Creates a ``StorageError``.
135
  public init(
136
    message: String,
137
    errorCode: StorageErrorCode,
138
    statusCode: Int? = nil,
139
    underlyingResponse: HTTPURLResponse? = nil,
140
    underlyingData: Data? = nil
NEW
141
  ) {
×
UNCOV
142
    self.message = message
×
NEW
143
    self.errorCode = errorCode
×
NEW
144
    self.statusCode = statusCode
×
NEW
145
    self.underlyingResponse = underlyingResponse
×
NEW
146
    self.underlyingData = underlyingData
×
UNCOV
147
  }
×
148
}
149

150
extension StorageError {
151
  /// `true` when the error indicates that the requested object or bucket does not exist.
152
  ///
153
  /// Covers HTTP status codes 400 and 404 as well as the explicit error codes
154
  /// ``StorageErrorCode/objectNotFound``, ``StorageErrorCode/bucketNotFound``, and
155
  /// ``StorageErrorCode/notFound``.
NEW
156
  public var isNotFound: Bool {
×
NEW
157
    statusCode == 400 || statusCode == 404
×
NEW
158
      || errorCode == .objectNotFound
×
NEW
159
      || errorCode == .bucketNotFound
×
NEW
160
      || errorCode == .notFound
×
NEW
161
  }
×
162

163
  /// `true` when the error indicates an authentication or authorisation failure (status 401 or 403).
NEW
164
  public var isUnauthorized: Bool {
×
NEW
165
    statusCode == 401 || statusCode == 403
×
NEW
166
  }
×
167
}
168

169
extension StorageError {
170
  /// Thrown when the signed upload URL returned by the server contains no upload token.
171
  public static let noTokenReturned = StorageError(
172
    message: "No token returned by API",
173
    errorCode: .noTokenReturned
174
  )
175
}
176

177
extension StorageError: LocalizedError {
178
  public var errorDescription: String? {
1✔
179
    message
1✔
180
  }
1✔
181
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc