CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-ktor--ktor-client-js

Ktor HTTP client implementation for JavaScript platform providing asynchronous HTTP client functionality specifically tailored for JavaScript environments.

Pending
Overview
Eval results
Files

error-handling.mddocs/

Error Handling

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.

Capabilities

JsError Class

Wrapper class for JavaScript error objects that provides consistent error handling across browser and Node.js environments.

/**
 * Wrapper for javascript error objects.
 * 
 * @property origin The original JavaScript error object
 */
class JsError(val origin: dynamic) : Throwable("Error from javascript[$origin].")

Usage Examples:

import io.ktor.client.*
import io.ktor.client.engine.js.*
import io.ktor.client.request.*

val client = HttpClient(Js)

try {
    val response = client.get("https://invalid-url")
} catch (error: JsError) {
    // Access the original JavaScript error
    println("JavaScript error: ${error.origin}")
    
    // The error message includes the original error
    println("Error message: ${error.message}")
    
    // Can inspect JavaScript error properties
    val jsError = error.origin
    console.log("Original JS error:", jsError)
}

WASM-JS Error Variant

For WebAssembly-JavaScript environments, a specialized variant provides type-safe error handling.

/**
 * WASM-JS specific wrapper for JavaScript error objects.
 * 
 * @property origin The original JavaScript error object as JsAny
 */
class JsError(val origin: JsAny) : Throwable("Error from javascript[$origin].")

Common Error Scenarios

Network Errors

import io.ktor.client.*
import io.ktor.client.engine.js.*
import io.ktor.client.request.*

val client = HttpClient(Js)

try {
    val response = client.get("https://unreachable-server.com")
} catch (error: JsError) {
    // Network-related JavaScript errors
    when {
        error.message?.contains("TypeError") == true -> {
            println("Network error: ${error.origin}")
        }
        error.message?.contains("Failed to fetch") == true -> {
            println("Fetch failed: ${error.origin}")
        }
        else -> {
            println("Unknown network error: ${error.origin}")
        }
    }
}

CORS Errors

try {
    val response = client.get("https://cors-restricted-api.com") {
        fetchOptions {
            mode = "cors"
        }
    }
} catch (error: JsError) {
    // CORS-related errors are wrapped as JsError
    println("CORS error occurred: ${error.origin}")
    
    // Original error might contain CORS-specific information
    val corsError = error.origin
    console.log("CORS details:", corsError)
}

Request Cancellation

import org.w3c.dom.AbortController

val controller = AbortController()

try {
    val response = client.get("https://slow-api.com") {
        fetchOptions {
            signal = controller.signal
        }
    }
} catch (error: JsError) {
    // Check if error is due to request cancellation
    val jsError = error.origin
    if (jsError.name == "AbortError") {
        println("Request was cancelled")
    } else {
        println("Other error: ${error.origin}")
    }
}

// Cancel the request
controller.abort()

Timeout Errors

import io.ktor.client.plugins.*

val client = HttpClient(Js) {
    install(HttpTimeout) {
        requestTimeoutMillis = 5000
    }
}

try {
    val response = client.get("https://very-slow-api.com")
} catch (timeout: HttpRequestTimeoutException) {
    // Ktor timeout exception
    println("Request timed out: ${timeout.message}")
} catch (error: JsError) {
    // JavaScript timeout or other errors
    println("JavaScript error: ${error.origin}")
}

Error Inspection Utilities

Examining JavaScript Error Properties

fun inspectJsError(error: JsError) {
    val jsError = error.origin
    
    // Common JavaScript error properties
    val name = jsError.name as? String
    val message = jsError.message as? String
    val stack = jsError.stack as? String
    
    println("Error name: $name")
    println("Error message: $message")
    println("Error stack: $stack")
    
    // Platform-specific properties
    when {
        name == "TypeError" -> println("Type error occurred")
        name == "NetworkError" -> println("Network issue detected")
        name == "AbortError" -> println("Request was aborted")
        name == "TimeoutError" -> println("Request timed out")
    }
}

// Usage
try {
    client.get("https://problematic-url.com")
} catch (error: JsError) {
    inspectJsError(error)
}

Logging JavaScript Errors

import io.ktor.client.plugins.logging.*

val client = HttpClient(Js) {
    install(Logging) {
        logger = Logger.DEFAULT
        level = LogLevel.INFO
    }
}

// Custom error logging
suspend fun safeRequest(url: String): String? {
    return try {
        val response = client.get(url)
        response.bodyAsText()
    } catch (error: JsError) {
        // Log the JavaScript error details
        console.error("JavaScript error in request to $url:", error.origin)
        
        // Return null or throw custom exception
        null
    }
}

Error Recovery Patterns

Retry with Exponential Backoff

import kotlinx.coroutines.delay
import kotlin.random.Random

suspend fun requestWithRetry(
    url: String,
    maxRetries: Int = 3,
    baseDelayMs: Long = 1000
): HttpResponse? {
    var attempt = 0
    
    while (attempt < maxRetries) {
        try {
            return client.get(url)
        } catch (error: JsError) {
            attempt++
            
            if (attempt >= maxRetries) {
                println("Max retries exceeded for $url")
                throw error
            }
            
            // Exponential backoff with jitter
            val delayMs = baseDelayMs * (1L shl attempt) + Random.nextLong(1000)
            println("Request failed, retrying in ${delayMs}ms (attempt $attempt)")
            delay(delayMs)
        }
    }
    
    return null
}

Fallback Mechanisms

suspend fun requestWithFallback(primaryUrl: String, fallbackUrl: String): HttpResponse {
    return try {
        client.get(primaryUrl)
    } catch (primaryError: JsError) {
        println("Primary request failed: ${primaryError.origin}")
        
        try {
            client.get(fallbackUrl)
        } catch (fallbackError: JsError) {
            println("Fallback request also failed: ${fallbackError.origin}")
            throw fallbackError
        }
    }
}

// Usage
val response = requestWithFallback(
    "https://primary-api.com/data",
    "https://backup-api.com/data"
)

Platform-Specific Error Handling

Browser-Specific Errors

fun handleBrowserErrors(error: JsError) {
    val jsError = error.origin
    
    when (jsError.name) {
        "SecurityError" -> {
            println("Security policy violation (CORS, mixed content, etc.)")
        }
        "NetworkError" -> {
            println("Network connectivity issue")
        }
        "QuotaExceededError" -> {
            println("Storage quota exceeded")
        }
        else -> {
            println("Other browser error: ${jsError.name}")
        }
    }
}

Node.js-Specific Errors

fun handleNodeErrors(error: JsError) {
    val jsError = error.origin
    val code = jsError.code as? String
    
    when (code) {
        "ENOTFOUND" -> {
            println("DNS resolution failed")
        }
        "ECONNREFUSED" -> {
            println("Connection refused by server")
        }
        "ETIMEDOUT" -> {
            println("Connection timed out")
        }
        "ECONNRESET" -> {
            println("Connection reset by peer")
        }
        else -> {
            println("Node.js error: $code - ${jsError.message}")
        }
    }
}

Universal Error Handler

fun handleUniversalError(error: JsError) {
    val jsError = error.origin
    val name = jsError.name as? String
    val message = jsError.message as? String
    
    // Log all error details
    console.log("Full error object:", jsError)
    
    // Handle common patterns
    when {
        name == "AbortError" -> println("Request was cancelled")
        message?.contains("Failed to fetch") == true -> println("Network request failed")
        message?.contains("CORS") == true -> println("CORS policy violation")
        name == "TypeError" && message?.contains("NetworkError") == true -> {
            println("Network error (possibly offline)")
        }
        else -> {
            println("Unhandled JavaScript error: $name - $message")
        }
    }
}

Best Practices

  1. Always Wrap HTTP Calls: Use try-catch blocks around HTTP requests to handle JsError exceptions

  2. Inspect Original Errors: Access the origin property to get detailed JavaScript error information

  3. Platform Detection: Handle browser vs Node.js specific error patterns differently

  4. Error Logging: Log JavaScript errors to external services for debugging production issues

  5. Graceful Degradation: Implement fallback mechanisms for network failures

  6. User-Friendly Messages: Convert technical JavaScript errors into user-understandable messages

Install with Tessl CLI

npx tessl i tessl/maven-io-ktor--ktor-client-js

docs

engine-configuration.md

error-handling.md

fetch-api-types.md

http-engine.md

index.md

request-configuration.md

tile.json