agentsclimarketplace

Swift error handling style workflow

Skill gaelic-ghost/socket/plugins/swift-lang/skills/swift-error-handling-style-workflow

The Source for macOS Agent Workflows

Install
npx -y skills add gaelic-ghost/socket --skill swift-error-handling-style-workflow

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Design or repair Swift error handling style using throws, typed throws, Result, Optional, AsyncSequence failure types, domain errors, Cocoa bridging, and concise functional recovery paths.

The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

9.9 KB, as published. Nobody here has run it

Swift Error Handling Style Workflow

Purpose

Make Swift failure behavior clear at the call site and useful when something breaks.

The house style is concise, typed by default for Swift-owned failure surfaces, and functional in feel: fallible values should move through explicit carriers, error messages should explain the failed operation, and recovery should happen at the boundary that can actually choose a next step.

Source Check

Use repo-local guidance first. For general language behavior, prefer the Swift Book, Swift Standard Library docs, Swift Evolution, and Apple Foundation docs:

When To Use

  • Use this skill when designing or reviewing Swift error surfaces.
  • Use this skill when code hides recoverable failures in nil, strings, logs, or broad catch-all wrappers.
  • Use this skill when deciding between throws, typed throws, Result, Optional, AsyncSequence failure types, framework errors, or domain errors.
  • Use this skill when modernizing nested do/catch, callback-era Result-passing, weak diagnostics, or awkward Objective-C/Cocoa error bridging.

Workflow

  1. Identify the failure boundary:
    • operation
    • inputs
    • success value
    • expected absence
    • recoverable failures
    • programmer errors
    • framework or transport errors
    • async or streaming boundary
  2. Choose the carrier:
    • nonoptional value when failure is impossible after construction
    • Optional when absence is expected and not diagnostic
    • typed throws for Swift-owned fallible operations when the error type can be named clearly
    • untyped throws or async throws when the operation forwards broad, open-ended framework, filesystem, networking, database, plugin, or dependency failures without adding a useful typed boundary
    • Result when success or failure must be stored, combined, cached, tested, or delivered through a non-throwing callback
    • AsyncSequence failure types when values arrive over time and iteration can fail
    • existing framework errors when the platform already gives a precise error domain
  3. Model domain failures:
    • prefer existing framework errors until a concrete custom domain, extension, or call-site recovery need appears
    • prefer small enum errors with associated values when the cases are closed and meaningful
    • preserve underlying errors when they help diagnosis
    • use LocalizedError for user-visible or operator-facing descriptions
    • use CustomNSError when Cocoa interop, error domains, codes, or user-info keys matter
    • use RecoverableError only when the caller can present concrete recovery choices
  4. Keep flow concise:
    • use try and try await for straight-line fallible work
    • use map, flatMap, mapError, Result.get(), and typed transforms when the failure value is intentionally part of the pipeline
    • prefer functional composition over imperative branching whenever it stays accurate and readable
    • split long chains at diagnostic, side-effect, actor, or async boundaries
    • catch narrowly where recovery happens
    • let errors propagate when the current layer has no useful recovery decision
  5. Improve diagnostics:
    • include operation, source, important input identity, likely cause, and next inspection point when the error reaches a human
    • keep low-level details available without dumping secrets or raw payloads
    • log at the boundary that has context, not at every propagation hop
    • avoid vague messages such as failed, invalid, or unknown error

House Defaults

  • Prefer typed throws for Swift-owned synchronous and structured-concurrency APIs when the error type can be named clearly.
  • Prefer untyped throws when forwarding broad framework, filesystem, networking, database, plugin, or dependency failures without changing their meaning.
  • Prefer Result for value-level composition, storage, callback interop, batch outcomes, and tests that need to assert failure as data.
  • Prefer Optional only for ordinary absence. Do not erase useful failure information to make a pipeline look tidy.
  • Prefer existing Foundation, Cocoa, SwiftPM, SwiftNIO, Vapor, Hummingbird, or framework error types until a concrete custom domain, extension, or recovery need appears.
  • Prefer small domain error enums over broad wrapper hierarchies when custom errors are needed.
  • Prefer preserving underlying errors over stringifying them.
  • Prefer direct propagation over local catch-and-rethrow wrappers that add no new context.
  • Prefer functional transforms, narrow recovery helpers, and value-level error composition over broad imperative branching.
  • Prefer assertions, preconditions, or non-throwing validation for programmer mistakes only when recovery is not part of the API contract.

Typed Throws Guidance

Typed throws is the preferred house style for Swift-owned error surfaces, while untyped throws remains the right tool for open-ended failure domains.

Use typed throws when:

  • the operation has a closed domain error set
  • the operation is Swift-owned and the error type can be named clearly
  • callers benefit from exhaustive catch handling
  • tests should assert every domain case
  • a generic API should preserve its caller's failure type
  • embedded, performance-sensitive, or allocation-sensitive code benefits from carrying a concrete error type

Avoid typed throws when:

  • the operation mostly forwards framework, filesystem, networking, database, or plugin errors without adding a meaningful typed boundary
  • the API boundary is public and the error set is likely to grow
  • callers would immediately erase the type to any Error
  • the type annotation makes simple code noisier without changing recovery

Error Helper Direction

A small shared helper package could become useful if several repositories start needing the same concise diagnostic, wrapping, or recovery helpers.

Treat that as a separate design decision. A future package might explore generic helpers, variadic generics or parameter packs, and macros, but do not invent a local helper framework inside one app or skill unless the repeated call sites already exist and the package design has been discussed.

Use the root Socket maintainer plan at docs/maintainers/errorhandles-package-plan.md when deciding whether that helper belongs in Socket or in a separate Swift package repository.

Example Shapes

Straight-line fallible work:

func loadManifest(at url: URL) async throws -> Manifest {
    let data = try await fetch(url)
    return try ManifestDecoder().decode(data)
}

Closed domain failures:

enum ManifestError: Error, Equatable {
    case missingName(URL)
    case unsupportedVersion(String)
}

func validate(_ manifest: Manifest) throws(ManifestError) -> Manifest {
    guard let name = manifest.name else {
        throw .missingName(manifest.sourceURL)
    }

    guard manifest.version.isSupported else {
        throw .unsupportedVersion(manifest.version.rawValue)
    }

    return manifest
}

Stored or batched failures:

let results: [Result<Package, PackageLoadError>] = urls.map { url in
    Result { try loadPackage(at: url) }
}

let packages = results.compactMap { try? $0.get() }
let failures = results.compactMap { result -> PackageLoadError? in
    guard case let .failure(error) = result else { return nil }
    return error
}

Operator-facing error context:

enum PackageLoadError: LocalizedError {
    case unreadableManifest(url: URL, underlying: any Error)

    var errorDescription: String? {
        switch self {
        case let .unreadableManifest(url, underlying):
            "Could not read Package.swift at \(url.path). Check that the file exists, is readable, and contains valid Swift package syntax. Underlying error: \(underlying)"
        }
    }
}

Output Shape

Return:

  1. Failure state: current operation, success value, absence, recoverable failures, and programmer errors.
  2. Carrier choice: why throws, typed throws, Result, Optional, AsyncSequence, existing framework errors, or domain errors fit.
  3. House-style changes: API signatures, error types, propagation, recovery, and diagnostics to change.
  4. Examples: compact call-site or implementation sketch.
  5. Validation: compile, tests, and failure-case checks needed.

Guardrails

  • Do not add error abstraction layers without a real caller, recovery path, or interop need.
  • Do not wrap every underlying error just to make a local enum exhaustive.
  • Do not force typed throws onto APIs whose failures are still genuinely open-ended.
  • Do not hide recoverable failures in logs, nil, default values, or comments.
  • Do not over-functionalize error handling when a narrow do/catch is clearer.
  • Do not catch only to print or log and then continue with corrupted state.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.