0
# Error Handling
1
2
JavaScript-specific error handling with wrapped native JavaScript errors for debugging and error reporting. The Ktor Client JS provides specialized error classes to handle JavaScript runtime errors consistently across different platforms.
3
4
## Capabilities
5
6
### JsError Class
7
8
Wrapper class for JavaScript error objects that provides consistent error handling across browser and Node.js environments.
9
10
```kotlin { .api }
11
/**
12
* Wrapper for javascript error objects.
13
*
14
* @property origin The original JavaScript error object
15
*/
16
class JsError(val origin: dynamic) : Throwable("Error from javascript[$origin].")
17
```
18
19
**Usage Examples:**
20
21
```kotlin
22
import io.ktor.client.*
23
import io.ktor.client.engine.js.*
24
import io.ktor.client.request.*
25
26
val client = HttpClient(Js)
27
28
try {
29
val response = client.get("https://invalid-url")
30
} catch (error: JsError) {
31
// Access the original JavaScript error
32
println("JavaScript error: ${error.origin}")
33
34
// The error message includes the original error
35
println("Error message: ${error.message}")
36
37
// Can inspect JavaScript error properties
38
val jsError = error.origin
39
console.log("Original JS error:", jsError)
40
}
41
```
42
43
### WASM-JS Error Variant
44
45
For WebAssembly-JavaScript environments, a specialized variant provides type-safe error handling.
46
47
```kotlin { .api }
48
/**
49
* WASM-JS specific wrapper for JavaScript error objects.
50
*
51
* @property origin The original JavaScript error object as JsAny
52
*/
53
class JsError(val origin: JsAny) : Throwable("Error from javascript[$origin].")
54
```
55
56
## Common Error Scenarios
57
58
### Network Errors
59
60
```kotlin
61
import io.ktor.client.*
62
import io.ktor.client.engine.js.*
63
import io.ktor.client.request.*
64
65
val client = HttpClient(Js)
66
67
try {
68
val response = client.get("https://unreachable-server.com")
69
} catch (error: JsError) {
70
// Network-related JavaScript errors
71
when {
72
error.message?.contains("TypeError") == true -> {
73
println("Network error: ${error.origin}")
74
}
75
error.message?.contains("Failed to fetch") == true -> {
76
println("Fetch failed: ${error.origin}")
77
}
78
else -> {
79
println("Unknown network error: ${error.origin}")
80
}
81
}
82
}
83
```
84
85
### CORS Errors
86
87
```kotlin
88
try {
89
val response = client.get("https://cors-restricted-api.com") {
90
fetchOptions {
91
mode = "cors"
92
}
93
}
94
} catch (error: JsError) {
95
// CORS-related errors are wrapped as JsError
96
println("CORS error occurred: ${error.origin}")
97
98
// Original error might contain CORS-specific information
99
val corsError = error.origin
100
console.log("CORS details:", corsError)
101
}
102
```
103
104
### Request Cancellation
105
106
```kotlin
107
import org.w3c.dom.AbortController
108
109
val controller = AbortController()
110
111
try {
112
val response = client.get("https://slow-api.com") {
113
fetchOptions {
114
signal = controller.signal
115
}
116
}
117
} catch (error: JsError) {
118
// Check if error is due to request cancellation
119
val jsError = error.origin
120
if (jsError.name == "AbortError") {
121
println("Request was cancelled")
122
} else {
123
println("Other error: ${error.origin}")
124
}
125
}
126
127
// Cancel the request
128
controller.abort()
129
```
130
131
### Timeout Errors
132
133
```kotlin
134
import io.ktor.client.plugins.*
135
136
val client = HttpClient(Js) {
137
install(HttpTimeout) {
138
requestTimeoutMillis = 5000
139
}
140
}
141
142
try {
143
val response = client.get("https://very-slow-api.com")
144
} catch (timeout: HttpRequestTimeoutException) {
145
// Ktor timeout exception
146
println("Request timed out: ${timeout.message}")
147
} catch (error: JsError) {
148
// JavaScript timeout or other errors
149
println("JavaScript error: ${error.origin}")
150
}
151
```
152
153
## Error Inspection Utilities
154
155
### Examining JavaScript Error Properties
156
157
```kotlin
158
fun inspectJsError(error: JsError) {
159
val jsError = error.origin
160
161
// Common JavaScript error properties
162
val name = jsError.name as? String
163
val message = jsError.message as? String
164
val stack = jsError.stack as? String
165
166
println("Error name: $name")
167
println("Error message: $message")
168
println("Error stack: $stack")
169
170
// Platform-specific properties
171
when {
172
name == "TypeError" -> println("Type error occurred")
173
name == "NetworkError" -> println("Network issue detected")
174
name == "AbortError" -> println("Request was aborted")
175
name == "TimeoutError" -> println("Request timed out")
176
}
177
}
178
179
// Usage
180
try {
181
client.get("https://problematic-url.com")
182
} catch (error: JsError) {
183
inspectJsError(error)
184
}
185
```
186
187
### Logging JavaScript Errors
188
189
```kotlin
190
import io.ktor.client.plugins.logging.*
191
192
val client = HttpClient(Js) {
193
install(Logging) {
194
logger = Logger.DEFAULT
195
level = LogLevel.INFO
196
}
197
}
198
199
// Custom error logging
200
suspend fun safeRequest(url: String): String? {
201
return try {
202
val response = client.get(url)
203
response.bodyAsText()
204
} catch (error: JsError) {
205
// Log the JavaScript error details
206
console.error("JavaScript error in request to $url:", error.origin)
207
208
// Return null or throw custom exception
209
null
210
}
211
}
212
```
213
214
## Error Recovery Patterns
215
216
### Retry with Exponential Backoff
217
218
```kotlin
219
import kotlinx.coroutines.delay
220
import kotlin.random.Random
221
222
suspend fun requestWithRetry(
223
url: String,
224
maxRetries: Int = 3,
225
baseDelayMs: Long = 1000
226
): HttpResponse? {
227
var attempt = 0
228
229
while (attempt < maxRetries) {
230
try {
231
return client.get(url)
232
} catch (error: JsError) {
233
attempt++
234
235
if (attempt >= maxRetries) {
236
println("Max retries exceeded for $url")
237
throw error
238
}
239
240
// Exponential backoff with jitter
241
val delayMs = baseDelayMs * (1L shl attempt) + Random.nextLong(1000)
242
println("Request failed, retrying in ${delayMs}ms (attempt $attempt)")
243
delay(delayMs)
244
}
245
}
246
247
return null
248
}
249
```
250
251
### Fallback Mechanisms
252
253
```kotlin
254
suspend fun requestWithFallback(primaryUrl: String, fallbackUrl: String): HttpResponse {
255
return try {
256
client.get(primaryUrl)
257
} catch (primaryError: JsError) {
258
println("Primary request failed: ${primaryError.origin}")
259
260
try {
261
client.get(fallbackUrl)
262
} catch (fallbackError: JsError) {
263
println("Fallback request also failed: ${fallbackError.origin}")
264
throw fallbackError
265
}
266
}
267
}
268
269
// Usage
270
val response = requestWithFallback(
271
"https://primary-api.com/data",
272
"https://backup-api.com/data"
273
)
274
```
275
276
## Platform-Specific Error Handling
277
278
### Browser-Specific Errors
279
280
```kotlin
281
fun handleBrowserErrors(error: JsError) {
282
val jsError = error.origin
283
284
when (jsError.name) {
285
"SecurityError" -> {
286
println("Security policy violation (CORS, mixed content, etc.)")
287
}
288
"NetworkError" -> {
289
println("Network connectivity issue")
290
}
291
"QuotaExceededError" -> {
292
println("Storage quota exceeded")
293
}
294
else -> {
295
println("Other browser error: ${jsError.name}")
296
}
297
}
298
}
299
```
300
301
### Node.js-Specific Errors
302
303
```kotlin
304
fun handleNodeErrors(error: JsError) {
305
val jsError = error.origin
306
val code = jsError.code as? String
307
308
when (code) {
309
"ENOTFOUND" -> {
310
println("DNS resolution failed")
311
}
312
"ECONNREFUSED" -> {
313
println("Connection refused by server")
314
}
315
"ETIMEDOUT" -> {
316
println("Connection timed out")
317
}
318
"ECONNRESET" -> {
319
println("Connection reset by peer")
320
}
321
else -> {
322
println("Node.js error: $code - ${jsError.message}")
323
}
324
}
325
}
326
```
327
328
### Universal Error Handler
329
330
```kotlin
331
fun handleUniversalError(error: JsError) {
332
val jsError = error.origin
333
val name = jsError.name as? String
334
val message = jsError.message as? String
335
336
// Log all error details
337
console.log("Full error object:", jsError)
338
339
// Handle common patterns
340
when {
341
name == "AbortError" -> println("Request was cancelled")
342
message?.contains("Failed to fetch") == true -> println("Network request failed")
343
message?.contains("CORS") == true -> println("CORS policy violation")
344
name == "TypeError" && message?.contains("NetworkError") == true -> {
345
println("Network error (possibly offline)")
346
}
347
else -> {
348
println("Unhandled JavaScript error: $name - $message")
349
}
350
}
351
}
352
```
353
354
## Best Practices
355
356
1. **Always Wrap HTTP Calls**: Use try-catch blocks around HTTP requests to handle `JsError` exceptions
357
358
2. **Inspect Original Errors**: Access the `origin` property to get detailed JavaScript error information
359
360
3. **Platform Detection**: Handle browser vs Node.js specific error patterns differently
361
362
4. **Error Logging**: Log JavaScript errors to external services for debugging production issues
363
364
5. **Graceful Degradation**: Implement fallback mechanisms for network failures
365
366
6. **User-Friendly Messages**: Convert technical JavaScript errors into user-understandable messages