diff --git a/Evolution/NNNN-map-error.md b/Evolution/NNNN-map-error.md
new file mode 100644
index 00000000..2d4c344a
--- /dev/null
+++ b/Evolution/NNNN-map-error.md
@@ -0,0 +1,85 @@
+# Map Error
+
+* Proposal: [SAA-NNNN](NNNN-map-error.md)
+* Authors: [Clive Liu](https://github.com/clive819)
+* Review Manager: TBD
+* Status: **Awaiting review**
+
+*During the review process, add the following fields as needed:*
+
+* Implementation: [apple/swift-async-algorithms#324](https://github.com/apple/swift-async-algorithms/pull/324)
+* Decision Notes: 
+* Bugs: 
+
+## Introduction
+
+The `mapError` function empowers developers to elegantly transform errors within asynchronous sequences, enhancing code readability and maintainability.
+
+```swift
+extension AsyncSequence {
+
+    public func mapError<MappedFailure: Error>(_ transform: @Sendable @escaping (Self.Failure) -> MappedFailure) -> some AsyncSequence<Self.Element, MappedFailure> {
+        AsyncMapErrorSequence(base: self, transform: transform)
+    }
+}
+```
+
+## Detailed design
+
+The function iterates through the elements of an `AsyncSequence` within a do-catch block. If an error is caught, it calls the `transform` closure to convert the error into a new type and then throws it.
+
+```swift
+struct AsyncMapErrorSequence<Base: AsyncSequence, MappedFailure: Error>: AsyncSequence {
+
+    ...
+
+    func makeAsyncIterator() -> Iterator {
+        Iterator(
+            base: base.makeAsyncIterator(),
+            transform: transform
+        )
+    }
+}
+
+extension AsyncMapErrorSequence {
+
+    struct Iterator: AsyncIteratorProtocol {
+
+        typealias Element = Base.Element
+
+        private var base: Base.AsyncIterator
+
+        private let transform: @Sendable (Failure) -> MappedFailure
+
+        init(
+            base: Base.AsyncIterator,
+            transform: @Sendable @escaping (Failure) -> MappedFailure
+        ) {
+            self.base = base
+            self.transform = transform
+        }
+
+        mutating func next() async throws(MappedFailure) -> Element? {
+            do {
+                return try await base.next(isolation: nil)
+            } catch {
+                throw transform(error)
+            }
+        }
+
+        mutating func next(isolation actor: isolated (any Actor)?) async throws(MappedFailure) -> Element? {
+            do {
+                return try await base.next(isolation: actor)
+            } catch {
+                throw transform(error)
+            }
+        }
+    }
+}
+
+extension AsyncMapErrorSequence: Sendable where Base: Sendable, Base.Element: Sendable {}
+```
+
+## Naming
+
+The naming follows to current method naming of the Combine [mapError](https://developer.apple.com/documentation/combine/publisher/maperror(_:)) method.
diff --git a/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift b/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift
new file mode 100644
index 00000000..75008f41
--- /dev/null
+++ b/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2024 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+
+#if compiler(>=6.0)
+extension AsyncSequence {
+
+    /// Converts any failure into a new error.
+    ///
+    /// - Parameter transform: A closure that takes the failure as a parameter and returns a new error.
+    /// - Returns: An asynchronous sequence that maps the error thrown into the one produced by the transform closure.
+    ///
+    /// Use the ``mapError(_:)`` operator when you need to replace one error type with another.
+    @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
+    public func mapError<MappedError: Error>(_ transform: @Sendable @escaping (Self.Failure) -> MappedError) -> some AsyncSequence<Self.Element, MappedError> {
+        AsyncMapErrorSequence(base: self, transform: transform)
+    }
+
+    /// Converts any failure into a new error.
+    ///
+    /// - Parameter transform: A closure that takes the failure as a parameter and returns a new error.
+    /// - Returns: An asynchronous sequence that maps the error thrown into the one produced by the transform closure.
+    ///
+    /// Use the ``mapError(_:)`` operator when you need to replace one error type with another.
+    @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
+    public func mapError<MappedError: Error>(_ transform: @Sendable @escaping (Self.Failure) -> MappedError) -> (some AsyncSequence<Self.Element, MappedError> & Sendable) where Self: Sendable, Self.Element: Sendable {
+        AsyncMapErrorSequence(base: self, transform: transform)
+    }
+}
+
+/// An asynchronous sequence that converts any failure into a new error.
+@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
+fileprivate struct AsyncMapErrorSequence<Base: AsyncSequence, MappedError: Error>: AsyncSequence {
+
+    typealias AsyncIterator = Iterator
+    typealias Element = Base.Element
+    typealias Failure = Base.Failure
+
+    private let base: Base
+    private let transform: @Sendable (Failure) -> MappedError
+
+    init(
+        base: Base,
+        transform: @Sendable @escaping (Failure) -> MappedError
+    ) {
+        self.base = base
+        self.transform = transform
+    }
+
+    func makeAsyncIterator() -> Iterator {
+        Iterator(
+            base: base.makeAsyncIterator(),
+            transform: transform
+        )
+    }
+}
+
+@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
+extension AsyncMapErrorSequence {
+
+    /// The iterator that produces elements of the map sequence.
+    fileprivate struct Iterator: AsyncIteratorProtocol {
+
+        typealias Element = Base.Element
+
+        private var base: Base.AsyncIterator
+
+        private let transform: @Sendable (Failure) -> MappedError
+
+        init(
+            base: Base.AsyncIterator,
+            transform: @Sendable @escaping (Failure) -> MappedError
+        ) {
+            self.base = base
+            self.transform = transform
+        }
+
+        mutating func next() async throws(MappedError) -> Element? {
+            try await self.next(isolation: nil)
+        }
+
+        mutating func next(isolation actor: isolated (any Actor)?) async throws(MappedError) -> Element? {
+            do {
+                return try await base.next(isolation: actor)
+            } catch {
+                throw transform(error)
+            }
+        }
+    }
+}
+
+@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
+extension AsyncMapErrorSequence: Sendable where Base: Sendable, Base.Element: Sendable {}
+
+@available(*, unavailable)
+extension AsyncMapErrorSequence.Iterator: Sendable {}
+#endif
diff --git a/Tests/AsyncAlgorithmsTests/TestMapError.swift b/Tests/AsyncAlgorithmsTests/TestMapError.swift
new file mode 100644
index 00000000..afe90e13
--- /dev/null
+++ b/Tests/AsyncAlgorithmsTests/TestMapError.swift
@@ -0,0 +1,105 @@
+import AsyncAlgorithms
+import XCTest
+
+#if compiler(>=6.0)
+@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
+final class TestMapError: XCTestCase {
+
+    func test_mapError() async throws {
+        let array: [Any] = [1, 2, 3, MyAwesomeError.originalError, 4, 5, 6]
+        let sequence = array.async
+            .map {
+                if let error = $0 as? Error {
+                    throw error
+                } else {
+                    $0 as! Int
+                }
+            }
+            .mapError { _ in
+                MyAwesomeError.mappedError
+            }
+
+        var results: [Int] = []
+
+        do {
+            for try await number in sequence {
+                results.append(number)
+            }
+            XCTFail("sequence should throw")
+        } catch {
+            XCTAssertEqual(error, .mappedError)
+        }
+
+        XCTAssertEqual(results, [1, 2, 3])
+    }
+
+    func test_mapError_cancellation() async throws {
+        let value = "test"
+        let source = Indefinite(value: value).async
+        let sequence = source
+            .map {
+                if $0 == "just to trick compiler that this may throw" {
+                    throw MyAwesomeError.originalError
+                } else {
+                    $0
+                }
+            }
+            .mapError { _ in
+                MyAwesomeError.mappedError
+            }
+
+        let finished = expectation(description: "finished")
+        let iterated = expectation(description: "iterated")
+
+        let task = Task {
+            var firstIteration = false
+            for try await el in sequence {
+                XCTAssertEqual(el, value)
+
+                if !firstIteration {
+                    firstIteration = true
+                    iterated.fulfill()
+                }
+            }
+            finished.fulfill()
+        }
+
+        // ensure the other task actually starts
+        await fulfillment(of: [iterated], timeout: 1.0)
+        // cancellation should ensure the loop finishes
+        // without regards to the remaining underlying sequence
+        task.cancel()
+        await fulfillment(of: [finished], timeout: 1.0)
+    }
+
+    func test_mapError_empty() async throws {
+        let array: [String] = []
+        let sequence = array.async
+            .map {
+                if $0 == "just to trick compiler that this may throw" {
+                    throw MyAwesomeError.originalError
+                } else {
+                    $0
+                }
+            }
+            .mapError { _ in
+                MyAwesomeError.mappedError
+            }
+
+        var results: [String] = []
+        for try await value in sequence {
+            results.append(value)
+        }
+        XCTAssert(results.isEmpty)
+    }
+}
+
+@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
+private extension TestMapError {
+
+    enum MyAwesomeError: Error {
+        case originalError
+        case mappedError
+    }
+}
+#endif