[dawn][webgpu.h] Use StringView in callback arguments.

This changes all callback const char* arguments to StringView instead.
To allow a semi-graceful deprecation, the webgpu_cpp.h template is
changed to temporarily support passing callbacks with const char* by
copying the StringView to a null-terminated allocation internally. A lot
of places in the code didn't use the C++ callbacks and need to be
updated though (in this repo, or in other repos with the breaking change
define).

Changes are:
 - Updates to dawn.json to update all callbacks to use StringView.
 - The addition of a breaking change define.
 - Updates to api_cpp.h to support const char* callbacks temporarily:
   - This required moving StringView up in the header, so it is now
     fully manually written instead of being partially generated.
   - For consistency api_structs.cpp/.h does the same.
 - dawn::native and dawn::wire are updated to pass sized string views
   instead of C strings to callbacks.
 - Small updates are done to mock_api, dawn.node, DawnWireServerFuzzer,
   samples and test harnesses when the C callbacks are used.
 - Tests are updated when they used the C API:
   - A new header is added that contains GMock matchers for
     WGPUStringView to make it easy to do StrEq equivalents etc, but
     also that check that strings are sized (as required by webgpu.h
     semantics).

Bug: 42241188
Change-Id: I3d04bfb3b80fd89773e74a04d2aef6ff161ec8f7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/209098
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/generator/templates/api.h b/generator/templates/api.h
index 05f8575..359b58c 100644
--- a/generator/templates/api.h
+++ b/generator/templates/api.h
@@ -39,6 +39,7 @@
 
 #define WGPU_BREAKING_CHANGE_STRING_VIEW_LABELS
 #define WGPU_BREAKING_CHANGE_STRING_VIEW_OUTPUT_STRUCTS
+#define WGPU_BREAKING_CHANGE_STRING_VIEW_CALLBACKS
 
 {% set API = metadata.c_prefix %}
 #if defined({{API}}_SHARED_LIBRARY)
diff --git a/generator/templates/api_cpp.h b/generator/templates/api_cpp.h
index fe78b71..0fca5aa 100644
--- a/generator/templates/api_cpp.h
+++ b/generator/templates/api_cpp.h
@@ -57,17 +57,6 @@
 
 namespace {{metadata.namespace}} {
 
-namespace detail {
-constexpr size_t ConstexprMax(size_t a, size_t b) {
-    return a > b ? a : b;
-}
-
-template <typename T>
-static T& AsNonConstReference(const T& value) {
-    return const_cast<T&>(value);
-}
-}  // namespace detail
-
 {% set c_prefix = metadata.c_prefix %}
 {% for constant in by_category["constant"] %}
     {% set type = as_cppType(constant.type.name) %}
@@ -340,7 +329,7 @@
     //* TODO: crbug.com/dawn/2509 - Remove name handling once old APIs are deprecated.
     {% set CallbackInfoType = (method.arguments|last).type %}
     {% set CallbackType = find_by_name(CallbackInfoType.members, "callback").type %}
-    {% set SfinaeArg = " = std::enable_if_t<std::is_convertible_v<F, Cb*>>" if not dfn else "" %}
+    {% set SfinaeArg = " = std::enable_if_t<std::is_convertible_v<F, Cb*> || std::is_convertible_v<F, CbChar*>>" if not dfn else "" %}
     template <typename F, typename T,
               typename Cb
                 {%- if not dfn -%}
@@ -350,6 +339,20 @@
                         {%- endfor -%}
                     T userdata)
                 {%- endif -%},
+              //* The Callback fnptr with const char* instead of StringView.
+              //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+              typename CbChar
+                {%- if not dfn -%}
+                    {{" "}}= void (
+                        {%- for arg in CallbackType.arguments -%}
+                            {%- if arg.type.name.canonical_case() == "string view" -%}
+                                const char* {{as_varName(arg.name)}}{{", "}}
+                            {%- else -%}
+                                {{as_annotated_cppType(arg)}}{{", "}}
+                            {%- endif -%}
+                        {%- endfor -%}
+                    T userdata)
+                {%- endif -%},
               typename{{SfinaeArg}}>
     {{as_cppType(method.return_type.name)}} {{MethodName}}(
         {%- for arg in method.arguments if arg.type.category != "callback info" -%}
@@ -372,7 +375,7 @@
     //* TODO: crbug.com/dawn/2509 - Remove name handling once old APIs are deprecated.
     {% set CallbackInfoType = (method.arguments|last).type %}
     {% set CallbackType = find_by_name(CallbackInfoType.members, "callback").type %}
-    {% set SfinaeArg = " = std::enable_if_t<std::is_convertible_v<L, Cb>>" if not dfn else "" %}
+    {% set SfinaeArg = " = std::enable_if_t<std::is_convertible_v<L, Cb> || std::is_convertible_v<L, CbChar>>" if not dfn else "" %}
     template <typename L,
               typename Cb
                 {%- if not dfn -%}
@@ -383,6 +386,21 @@
                         {%- endfor -%}
                     )>
                 {%- endif -%},
+              //* The Callback fnptr with const char* instead of StringView.
+              //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+              typename CbChar
+                {%- if not dfn -%}
+                    {{" "}}= std::function<void(
+                        {%- for arg in CallbackType.arguments -%}
+                            {%- if not loop.first %}, {% endif -%}
+                            {%- if arg.type.name.canonical_case() == "string view" -%}
+                                const char* {{as_varName(arg.name)}}
+                            {%- else -%}
+                                {{as_annotated_cppType(arg)}}
+                            {%- endif -%}
+                        {%- endfor -%}
+                    )>
+                {%- endif -%},
               typename{{SfinaeArg}}>
     {{as_cppType(method.return_type.name)}} {{MethodName}}(
         {%- for arg in method.arguments if arg.type.category != "callback info" -%}
@@ -440,24 +458,96 @@
     struct {{as_cppType(type.name)}};
 {% endfor %}
 
+// TODO(42241188): Remove once all clients use StringView versions of the callbacks.
+// To make MSVC happy we need a StringView constructor from the adapter, so we first need to
+// forward declare StringViewAdapter here. Otherwise MSVC complains about an ambiguous conversion.
+namespace detail {
+    struct StringViewAdapter;
+}  // namespace detail
+
+struct StringView {
+    char const * data = nullptr;
+    size_t length = WGPU_STRLEN;
+
+    {{wgpu_string_members("StringView") | indent(4)}}
+
+    StringView(const detail::StringViewAdapter& s);
+};
+
+namespace detail {
+constexpr size_t ConstexprMax(size_t a, size_t b) {
+    return a > b ? a : b;
+}
+
+template <typename T>
+static T& AsNonConstReference(const T& value) {
+    return const_cast<T&>(value);
+}
+
+// A wrapper around StringView that can be implicitly converted to const char* with temporary
+// storage that adds the \0 for output strings that are all explicitly-sized.
+// TODO(42241188): Remove once all clients use StringView versions of the callbacks.
+struct StringViewAdapter {
+    WGPUStringView sv;
+    char* nullTerminated = nullptr;
+
+    StringViewAdapter(WGPUStringView sv) : sv(sv) {}
+    ~StringViewAdapter() { delete[] nullTerminated; }
+    operator ::WGPUStringView() { return sv; }
+    operator StringView() { return {sv.data, sv.length}; }
+    operator const char*() {
+        assert(sv.length != WGPU_STRLEN);
+        assert(nullTerminated == nullptr);
+        nullTerminated = new char[sv.length + 1];
+        memcpy(nullTerminated, sv.data, sv.length);
+        nullTerminated[sv.length] = 0;
+        return nullTerminated;
+    }
+};
+}  // namespace detail
+
+inline StringView::StringView(const detail::StringViewAdapter& s): data(s.sv.data), length(s.sv.length) {}
+
 {% macro render_cpp_callback_info_template_method_impl(type, method) %}
     {{render_cpp_callback_info_template_method_declaration(type, method, dfn=True)}} {
         {% set CallbackInfoType = (method.arguments|last).type %}
         {% set CallbackType = find_by_name(CallbackInfoType.members, "callback").type %}
         {{as_cType(CallbackInfoType.name)}} callbackInfo = {};
         callbackInfo.mode = static_cast<{{as_cType(types["callback mode"].name)}}>(callbackMode);
-        callbackInfo.callback = [](
-            {%- for arg in CallbackType.arguments -%}
-                {{as_annotated_cType(arg)}}{{", "}}
-            {%- endfor -%}
-        void* callback, void* userdata) {
-            auto cb = reinterpret_cast<Cb*>(callback);
-            (*cb)(
+        if constexpr (std::is_convertible_v<F, Cb*>) {
+            callbackInfo.callback = [](
                 {%- for arg in CallbackType.arguments -%}
-                    {{convert_cType_to_cppType(arg.type, arg.annotation, as_varName(arg.name))}}{{", "}}
+                    {{as_annotated_cType(arg)}}{{", "}}
                 {%- endfor -%}
-            static_cast<T>(userdata));
-        };
+            void* callback, void* userdata) {
+                auto cb = reinterpret_cast<Cb*>(callback);
+                (*cb)(
+                    {%- for arg in CallbackType.arguments -%}
+                        {{convert_cType_to_cppType(arg.type, arg.annotation, as_varName(arg.name))}}{{", "}}
+                    {%- endfor -%}
+                static_cast<T>(userdata));
+            };
+        } else {
+            //* Handle functors that take in const char* instead of StringView.
+            //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+            //* and also remove CbChar at the same time.
+            callbackInfo.callback = [](
+                {%- for arg in CallbackType.arguments -%}
+                    {{as_annotated_cType(arg)}}{{", "}}
+                {%- endfor -%}
+            void* callback, void* userdata) {
+                auto cb = reinterpret_cast<CbChar*>(callback);
+                (*cb)(
+                    {%- for arg in CallbackType.arguments -%}
+                        {%- if arg.type.name.canonical_case() == "string view" -%}
+                            {detail::StringViewAdapter({{as_varName(arg.name)}})}{{", "}}
+                        {%- else -%}
+                            {{convert_cType_to_cppType(arg.type, arg.annotation, as_varName(arg.name))}}{{", "}}
+                        {%- endif -%}
+                    {%- endfor -%}
+                static_cast<T>(userdata));
+            };
+        }
         callbackInfo.userdata1 = reinterpret_cast<void*>(+callback);
         callbackInfo.userdata2 = reinterpret_cast<void*>(userdata);
         auto result = {{as_cMethodNamespaced(type.name, method.name, c_namespace)}}(Get(){{", "}}
@@ -514,7 +604,14 @@
                 (*lambda)(
                     {%- for arg in CallbackType.arguments -%}
                         {%- if not loop.first %}, {% endif -%}
-                        {{convert_cType_to_cppType(arg.type, arg.annotation, as_varName(arg.name))}}
+                        //* Handle functors that take in const char* instead of StringView.
+                        //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+                        //* and also remove CbChar at the same time.
+                        {%- if arg.type.name.canonical_case() == "string view" -%}
+                            {detail::StringViewAdapter({{as_varName(arg.name)}})}
+                        {%- else -%}
+                            {{convert_cType_to_cppType(arg.type, arg.annotation, as_varName(arg.name))}}
+                        {%- endif -%}
                     {%- endfor -%});
             };
             callbackInfo.userdata1 = reinterpret_cast<void*>(lambda);
@@ -584,7 +681,7 @@
     "offsetof mismatch for ChainedStruct::sType");
 
 //* Special structures that require some custom code generation.
-{% set SpecialStructures = ["device descriptor"] %}
+{% set SpecialStructures = ["device descriptor", "string view"] %}
 
 {% for type in by_category["structure"] if type.name.get() not in SpecialStructures %}
     {% set Out = "Out" if type.output else "" %}
@@ -627,18 +724,10 @@
                 {{member_declaration}};
             {% endif %}
         {% endfor %}
-
-        //* Custom string constructors
-        {% if type.name.get() == "string view" %}
-            {{wgpu_string_members(as_cppType(type.name)) | indent(4)}}
-        {% endif %}
-
         {% if type.has_free_members_function %}
 
           private:
-        {% if type.has_free_members_function %}
-                inline void FreeMembers();
-        {% endif %}
+            inline void FreeMembers();
             static inline void Reset({{as_cppType(type.name)}}& value);
         {% endif %}
     };
@@ -678,21 +767,29 @@
     inline {{CppType}}(Init&& init);
 
     template <typename F, typename T,
-              typename Cb = void (const Device& device, DeviceLostReason reason, const char * message, T userdata),
-              typename = std::enable_if_t<std::is_convertible_v<F, Cb*>>>
+              typename Cb = void (const Device& device, DeviceLostReason reason, StringView message, T userdata),
+              //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+              typename CbChar = void (const Device& device, DeviceLostReason reason, const char* message, T userdata),
+              typename = std::enable_if_t<std::is_convertible_v<F, Cb*> || std::is_convertible_v<F, CbChar*>>>
     void SetDeviceLostCallback(CallbackMode callbackMode, F callback, T userdata);
     template <typename L,
-              typename Cb = std::function<void(const Device& device, DeviceLostReason reason, const char * message)>,
-              typename = std::enable_if_t<std::is_convertible_v<L, Cb>>>
+              typename Cb = std::function<void(const Device& device, DeviceLostReason reason, StringView message)>,
+              //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+              typename CbChar = std::function<void(const Device& device, DeviceLostReason reason, const char* message)>,
+              typename = std::enable_if_t<std::is_convertible_v<L, Cb> || std::is_convertible_v<L, CbChar>>>
     void SetDeviceLostCallback(CallbackMode callbackMode, L callback);
 
     template <typename F, typename T,
-              typename Cb = void (const Device& device, ErrorType type, const char * message, T userdata),
-              typename = std::enable_if_t<std::is_convertible_v<F, Cb*>>>
+              typename Cb = void (const Device& device, ErrorType type, StringView message, T userdata),
+              //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+              typename CbChar = void (const Device& device, ErrorType type, const char* message, T userdata),
+              typename = std::enable_if_t<std::is_convertible_v<F, Cb*> || std::is_convertible_v<F, CbChar*>>>
     void SetUncapturedErrorCallback(F callback, T userdata);
     template <typename L,
-              typename Cb = std::function<void(const Device& device, ErrorType type, const char * message)>,
-              typename = std::enable_if_t<std::is_convertible_v<L, Cb>>>
+              typename Cb = std::function<void(const Device& device, ErrorType type, StringView message)>,
+              //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+              typename CbChar = std::function<void(const Device& device, ErrorType type, const char* message)>,
+              typename = std::enable_if_t<std::is_convertible_v<L, Cb> || std::is_convertible_v<L, CbChar>>>
     void SetUncapturedErrorCallback(L callback);
 };
 
@@ -747,7 +844,7 @@
             }
             FreeMembers();
             {% for member in type.members %}
-                ::{{metadata.namespace}}::detail::AsNonConstReference(this->{{member.name.camelCase()}}) = std::move(rhs.{{member.name.camelCase()}});
+                detail::AsNonConstReference(this->{{member.name.camelCase()}}) = std::move(rhs.{{member.name.camelCase()}});
             {% endfor %}
             Reset(rhs);
             return *this;
@@ -773,7 +870,7 @@
         void {{CppType}}::Reset({{CppType}}& value) {
             {{CppType}} defaultValue{};
             {% for member in type.members %}
-                ::{{metadata.namespace}}::detail::AsNonConstReference(value.{{member.name.camelCase()}}) = defaultValue.{{member.name.camelCase()}};
+                detail::AsNonConstReference(value.{{member.name.camelCase()}}) = defaultValue.{{member.name.camelCase()}};
             {% endfor %}
         }
     {% endif %}
@@ -833,30 +930,41 @@
 static_assert(sizeof({{CppType}}) == sizeof({{CType}}), "sizeof mismatch for {{CppType}}");
 static_assert(alignof({{CppType}}) == alignof({{CType}}), "alignof mismatch for {{CppType}}");
 
-template <typename F, typename T, typename Cb, typename>
+template <typename F, typename T, typename Cb, typename CbChar, typename>
 void {{CppType}}::SetDeviceLostCallback(CallbackMode callbackMode, F callback, T userdata) {
     assert(deviceLostCallbackInfo2.callback == nullptr);
 
     deviceLostCallbackInfo2.mode = static_cast<WGPUCallbackMode>(callbackMode);
-    deviceLostCallbackInfo2.callback = [](WGPUDevice const * device, WGPUDeviceLostReason reason, char const * message, void* callback, void* userdata) {
-        auto cb = reinterpret_cast<Cb*>(callback);
-        // We manually acquire and release the device to avoid changing any ref counts.
-        auto apiDevice = Device::Acquire(*device);
-        (*cb)(apiDevice, static_cast<DeviceLostReason>(reason), message, static_cast<T>(userdata));
-        apiDevice.MoveToCHandle();
-    };
+    if constexpr (std::is_convertible_v<F, Cb*>) {
+        deviceLostCallbackInfo2.callback = [](WGPUDevice const * device, WGPUDeviceLostReason reason, WGPUStringView message, void* callback, void* userdata) {
+            auto cb = reinterpret_cast<Cb*>(callback);
+            // We manually acquire and release the device to avoid changing any ref counts.
+            auto apiDevice = Device::Acquire(*device);
+            (*cb)(apiDevice, static_cast<DeviceLostReason>(reason), message, static_cast<T>(userdata));
+            apiDevice.MoveToCHandle();
+        };
+    } else {
+         //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+        deviceLostCallbackInfo2.callback = [](WGPUDevice const * device, WGPUDeviceLostReason reason, WGPUStringView message, void* callback, void* userdata) {
+            auto cb = reinterpret_cast<CbChar*>(callback);
+            // We manually acquire and release the device to avoid changing any ref counts.
+            auto apiDevice = Device::Acquire(*device);
+            (*cb)(apiDevice, static_cast<DeviceLostReason>(reason), detail::StringViewAdapter(message), static_cast<T>(userdata));
+            apiDevice.MoveToCHandle();
+        };
+    }
     deviceLostCallbackInfo2.userdata1 = reinterpret_cast<void*>(+callback);
     deviceLostCallbackInfo2.userdata2 = reinterpret_cast<void*>(userdata);
 }
 
-template <typename L, typename Cb, typename>
+template <typename L, typename Cb, typename CbChar, typename>
 void {{CppType}}::SetDeviceLostCallback(CallbackMode callbackMode, L callback) {
     assert(deviceLostCallbackInfo2.callback == nullptr);
-    using F = void (const Device& device, DeviceLostReason reason, const char * message);
+    using F = void (const Device& device, DeviceLostReason reason, StringView message);
 
     deviceLostCallbackInfo2.mode = static_cast<WGPUCallbackMode>(callbackMode);
     if constexpr (std::is_convertible_v<L, F*>) {
-        deviceLostCallbackInfo2.callback = [](WGPUDevice const * device, WGPUDeviceLostReason reason, char const * message, void* callback, void*) {
+        deviceLostCallbackInfo2.callback = [](WGPUDevice const * device, WGPUDeviceLostReason reason, WGPUStringView message, void* callback, void*) {
             auto cb = reinterpret_cast<F*>(callback);
             // We manually acquire and release the device to avoid changing any ref counts.
             auto apiDevice = Device::Acquire(*device);
@@ -867,11 +975,11 @@
         deviceLostCallbackInfo2.userdata2 = nullptr;
     } else {
         auto* lambda = new L(std::move(callback));
-        deviceLostCallbackInfo2.callback = [](WGPUDevice const * device, WGPUDeviceLostReason reason, char const * message, void* callback, void*) {
+        deviceLostCallbackInfo2.callback = [](WGPUDevice const * device, WGPUDeviceLostReason reason, WGPUStringView message, void* callback, void*) {
             std::unique_ptr<L> lambda(reinterpret_cast<L*>(callback));
             // We manually acquire and release the device to avoid changing any ref counts.
             auto apiDevice = Device::Acquire(*device);
-            (*lambda)(apiDevice, static_cast<DeviceLostReason>(reason), message);
+            (*lambda)(apiDevice, static_cast<DeviceLostReason>(reason), detail::StringViewAdapter(message));
             apiDevice.MoveToCHandle();
         };
         deviceLostCallbackInfo2.userdata1 = reinterpret_cast<void*>(lambda);
@@ -879,31 +987,54 @@
     }
 }
 
-template <typename F, typename T, typename Cb, typename>
+template <typename F, typename T, typename Cb, typename CbChar, typename>
 void {{CppType}}::SetUncapturedErrorCallback(F callback, T userdata) {
-    uncapturedErrorCallbackInfo2.callback = [](WGPUDevice const * device, WGPUErrorType type, char const * message, void* callback, void* userdata) {
-        auto cb = reinterpret_cast<Cb*>(callback);
-        // We manually acquire and release the device to avoid changing any ref counts.
-        auto apiDevice = Device::Acquire(*device);
-        (*cb)(apiDevice, static_cast<ErrorType>(type), message, static_cast<T>(userdata));
-        apiDevice.MoveToCHandle();
-    };
+    if constexpr (std::is_convertible_v<F, Cb*>) {
+        uncapturedErrorCallbackInfo2.callback = [](WGPUDevice const * device, WGPUErrorType type, WGPUStringView message, void* callback, void* userdata) {
+            auto cb = reinterpret_cast<Cb*>(callback);
+            // We manually acquire and release the device to avoid changing any ref counts.
+            auto apiDevice = Device::Acquire(*device);
+            (*cb)(apiDevice, static_cast<ErrorType>(type), message, static_cast<T>(userdata));
+            apiDevice.MoveToCHandle();
+        };
+    } else {
+        //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+        uncapturedErrorCallbackInfo2.callback = [](WGPUDevice const * device, WGPUErrorType type, WGPUStringView message, void* callback, void* userdata) {
+            auto cb = reinterpret_cast<CbChar*>(callback);
+            // We manually acquire and release the device to avoid changing any ref counts.
+            auto apiDevice = Device::Acquire(*device);
+            (*cb)(apiDevice, static_cast<ErrorType>(type), detail::StringViewAdapter(message), static_cast<T>(userdata));
+            apiDevice.MoveToCHandle();
+        };
+    }
     uncapturedErrorCallbackInfo2.userdata1 = reinterpret_cast<void*>(+callback);
     uncapturedErrorCallbackInfo2.userdata2 = reinterpret_cast<void*>(userdata);
 }
 
-template <typename L, typename Cb, typename>
+template <typename L, typename Cb, typename CbChar, typename>
 void {{CppType}}::SetUncapturedErrorCallback(L callback) {
-    using F = void (const Device& device, ErrorType type, const char * message);
-    static_assert(std::is_convertible_v<L, F*>, "Uncaptured error callback cannot be a binding lambda");
+    using F = void (const Device& device, ErrorType type, StringView message);
+    using FChar = void (const Device& device, ErrorType type, const char* message);
+    static_assert(std::is_convertible_v<L, F*> || std::is_convertible_v<L, FChar*>, "Uncaptured error callback cannot be a binding lambda");
 
-    uncapturedErrorCallbackInfo2.callback = [](WGPUDevice const * device, WGPUErrorType type, char const * message, void* callback, void*) {
-        auto cb = reinterpret_cast<F*>(callback);
-        // We manually acquire and release the device to avoid changing any ref counts.
-        auto apiDevice = Device::Acquire(*device);
-        (*cb)(apiDevice, static_cast<ErrorType>(type), message);
-        apiDevice.MoveToCHandle();
-    };
+    if constexpr (std::is_convertible_v<L, F*>) {
+        uncapturedErrorCallbackInfo2.callback = [](WGPUDevice const * device, WGPUErrorType type, WGPUStringView message, void* callback, void*) {
+            auto cb = reinterpret_cast<F*>(callback);
+            // We manually acquire and release the device to avoid changing any ref counts.
+            auto apiDevice = Device::Acquire(*device);
+            (*cb)(apiDevice, static_cast<ErrorType>(type), message);
+            apiDevice.MoveToCHandle();
+        };
+    } else {
+        //* TODO(42241188): Remove once all clients use StringView versions of the callbacks
+        uncapturedErrorCallbackInfo2.callback = [](WGPUDevice const * device, WGPUErrorType type, WGPUStringView message, void* callback, void*) {
+            auto cb = reinterpret_cast<FChar*>(callback);
+            // We manually acquire and release the device to avoid changing any ref counts.
+            auto apiDevice = Device::Acquire(*device);
+            (*cb)(apiDevice, static_cast<ErrorType>(type), detail::StringViewAdapter(message));
+            apiDevice.MoveToCHandle();
+        };
+    }
     uncapturedErrorCallbackInfo2.userdata1 = reinterpret_cast<void*>(+callback);
     uncapturedErrorCallbackInfo2.userdata2 = nullptr;
 }
diff --git a/generator/templates/dawn/native/api_structs.cpp b/generator/templates/dawn/native/api_structs.cpp
index 64e373b..921def6 100644
--- a/generator/templates/dawn/native/api_structs.cpp
+++ b/generator/templates/dawn/native/api_structs.cpp
@@ -54,7 +54,14 @@
     static_assert(offsetof(ChainedStruct, sType) == offsetof({{c_prefix}}ChainedStruct, sType),
             "offsetof mismatch for ChainedStruct::sType");
 
-    {% for type in by_category["structure"] %}
+    //* Special structures that are manually written.
+    {% set SpecialStructures = ["string view"] %}
+
+    bool StringView::operator==(const StringView& rhs) const {
+        return data == rhs.data && length == rhs.length;
+    }
+
+    {% for type in by_category["structure"] if type.name.get() not in SpecialStructures %}
         {% set CppType = as_cppType(type.name) %}
         {% set CType = as_cType(type.name) %}
 
diff --git a/generator/templates/dawn/native/api_structs.h b/generator/templates/dawn/native/api_structs.h
index 0affb83..fe5bec4 100644
--- a/generator/templates/dawn/native/api_structs.h
+++ b/generator/templates/dawn/native/api_structs.h
@@ -68,7 +68,34 @@
     using {{namespace}}::ChainedStruct;
     using {{namespace}}::ChainedStructOut;
 
-    {% for type in by_category["structure"] %}
+    //* Special structures that are manually written.
+    {% set SpecialStructures = ["string view"] %}
+
+    struct StringView {
+        char const * data = nullptr;
+        size_t length = WGPU_STRLEN;
+
+        {{wgpu_string_members("StringView")}}
+
+        // Equality operators, mostly for testing. Note that this tests
+        // strict pointer-pointer equality if the struct contains member pointers.
+        bool operator==(const StringView& rhs) const;
+
+        #ifndef ABSL_USES_STD_STRING_VIEW
+        // NOLINTNEXTLINE(runtime/explicit) allow implicit conversion
+        operator absl::string_view() const {
+            if (this->length == wgpu::kStrlen) {
+                if (IsUndefined()) {
+                    return {};
+                }
+                return {this->data};
+            }
+            return {this->data, this->length};
+        }
+        #endif
+    };
+
+    {% for type in by_category["structure"] if type.name.get() not in SpecialStructures %}
         {% set CppType = as_cppType(type.name) %}
         {% if type.chained %}
             {% set chainedStructType = "ChainedStructOut" if type.chained == "out" else "ChainedStruct" %}
@@ -105,24 +132,6 @@
                 {% endif %}
             {% endfor %}
 
-            //* Custom string constructors / conversion
-            {% if type.name.get() == "string view" %}
-                {{wgpu_string_members(CppType) | indent(8)}}
-
-                #ifndef ABSL_USES_STD_STRING_VIEW
-                // NOLINTNEXTLINE(runtime/explicit) allow implicit conversion
-                operator absl::string_view() const {
-                    if (this->length == wgpu::kStrlen) {
-                        if (IsUndefined()) {
-                            return {};
-                        }
-                        return {this->data};
-                    }
-                    return {this->data, this->length};
-                }
-                #endif
-            {% endif %}
-
             {% if type.any_member_requires_struct_defaulting %}
                 // This method makes a copy of the struct, then, for any enum members with trivial
                 // defaulting (where something like "Undefined" is replaced with a default), applies
diff --git a/generator/templates/mock_api.cpp b/generator/templates/mock_api.cpp
index 7e28170..dc5c9e1 100644
--- a/generator/templates/mock_api.cpp
+++ b/generator/templates/mock_api.cpp
@@ -228,7 +228,7 @@
                                                    void* userdata) {
     ProcTableAsClass::Object* object = reinterpret_cast<ProcTableAsClass::Object*>(device);
     object->mDeviceLostCallback = [](WGPUDevice const*, WGPUDeviceLostReason reason,
-                                     char const* message, void* callback, void* userdata) {
+                                     WGPUStringView message, void* callback, void* userdata) {
         if (callback == nullptr) {
             return;
         }
@@ -242,7 +242,7 @@
 }
 void ProcTableAsClass::CallDeviceSetDeviceLostCallbackCallback(WGPUDevice device,
                                                                WGPUDeviceLostReason reason,
-                                                               char const* message) {
+                                                               WGPUStringView message) {
     ProcTableAsClass::Object* object = reinterpret_cast<ProcTableAsClass::Object*>(device);
     object->mDeviceLostCallback(&device, reason, message, object->mDeviceLostUserdata1,
                                 object->mDeviceLostUserdata2);
@@ -252,7 +252,7 @@
                                                         void* userdata) {
     ProcTableAsClass::Object* object = reinterpret_cast<ProcTableAsClass::Object*>(device);
     object->mUncapturedErrorCallback = [](WGPUDevice const*, WGPUErrorType type,
-                                          char const* message, void* callback, void* userdata) {
+                                          WGPUStringView message, void* callback, void* userdata) {
         if (callback == nullptr) {
             return;
         }
@@ -266,7 +266,7 @@
 }
 void ProcTableAsClass::CallDeviceSetUncapturedErrorCallbackCallback(WGPUDevice device,
                                                                     WGPUErrorType type,
-                                                                    char const* message) {
+                                                                    WGPUStringView message) {
     ProcTableAsClass::Object* object = reinterpret_cast<ProcTableAsClass::Object*>(device);
     object->mUncapturedErrorCallback(&device, type, message, object->mUncapturedErrorUserdata1,
                                      object->mUncapturedErrorUserdata2);
diff --git a/generator/templates/mock_api.h b/generator/templates/mock_api.h
index acaf37f..f13ebdf 100644
--- a/generator/templates/mock_api.h
+++ b/generator/templates/mock_api.h
@@ -152,7 +152,7 @@
                                                    void* userdata) = 0;
         void CallDeviceSetDeviceLostCallbackCallback(WGPUDevice device,
                                                      WGPUDeviceLostReason reason,
-                                                     char const* message);
+                                                     WGPUStringView message);
         void DeviceSetUncapturedErrorCallback(WGPUDevice device,
                                               WGPUErrorCallback callback,
                                               void* userdata);
@@ -161,7 +161,7 @@
                                                         void* userdata) = 0;
         void CallDeviceSetUncapturedErrorCallbackCallback(WGPUDevice device,
                                                           WGPUErrorType type,
-                                                          char const* message);
+                                                          WGPUStringView message);
 
         struct Object {
             ProcTableAsClass* procs = nullptr;
diff --git a/src/dawn/common/StringViewUtils.cpp b/src/dawn/common/StringViewUtils.cpp
index 63a4a51..1c0b650 100644
--- a/src/dawn/common/StringViewUtils.cpp
+++ b/src/dawn/common/StringViewUtils.cpp
@@ -47,10 +47,6 @@
     return {s.data(), s.size()};
 }
 
-WGPUStringView ToOutputStringView(const char* s) {
-    return {s, std::strlen(s)};
-}
-
 std::string ToString(WGPUStringView s) {
     if (s.length == WGPU_STRLEN) {
         if (s.data == nullptr) {
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index 2a9da95..c9caf55 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -85,7 +85,7 @@
         "args": [
             {"name": "status", "type": "request adapter status"},
             {"name": "adapter", "type": "adapter", "optional": true},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
+            {"name": "message", "type": "string view"},
             {"name": "userdata", "type": "void *"}
         ]
     },
@@ -94,7 +94,7 @@
         "args": [
             {"name": "status", "type": "request adapter status"},
             {"name": "adapter", "type": "adapter", "optional": true},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}
+            {"name": "message", "type": "string view"}
         ]
     },
     "request adapter callback info": {
@@ -740,7 +740,7 @@
         "category": "callback function",
         "args": [
             {"name": "status", "type": "map async status"},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"}
+            {"name": "message", "type": "string view"}
         ]
     },
     "buffer map callback info": {
@@ -1306,7 +1306,7 @@
         "args": [
             {"name": "status", "type": "create pipeline async status"},
             {"name": "pipeline", "type": "compute pipeline", "optional": true},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
+            {"name": "message", "type": "string view"},
             {"name": "userdata", "type": "void *"}
         ]
     },
@@ -1315,7 +1315,7 @@
         "args": [
             {"name": "status", "type": "create pipeline async status"},
             {"name": "pipeline", "type": "compute pipeline", "optional": true},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}
+            {"name": "message", "type": "string view"}
         ]
     },
     "create compute pipeline async callback info": {
@@ -1352,7 +1352,7 @@
         "args": [
             {"name": "status", "type": "create pipeline async status"},
             {"name": "pipeline", "type": "render pipeline", "optional": true},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
+            {"name": "message", "type": "string view"},
             {"name": "userdata", "type": "void *"}
         ]
     },
@@ -1361,7 +1361,7 @@
         "args": [
             {"name": "status", "type": "create pipeline async status"},
             {"name": "pipeline", "type": "render pipeline", "optional": true},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}
+            {"name": "message", "type": "string view"}
         ]
     },
     "create render pipeline async callback info": {
@@ -1780,7 +1780,7 @@
         "category": "function pointer",
         "args": [
             {"name": "reason", "type": "device lost reason"},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"},
+            {"name": "message", "type": "string view"},
             {"name": "userdata", "type": "void *"}
         ]
     },
@@ -1789,7 +1789,7 @@
         "args": [
             {"name": "device", "type": "device", "annotation": "const*", "length": 1},
             {"name": "reason", "type": "device lost reason"},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"},
+            {"name": "message", "type": "string view"},
             {"name": "userdata", "type": "void *"}
         ]
     },
@@ -1798,7 +1798,7 @@
         "args": [
             {"name": "device", "type": "device", "annotation": "const*", "length": 1},
             {"name": "reason", "type": "device lost reason"},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"}
+            {"name": "message", "type": "string view"}
         ]
     },
     "device lost callback info": {
@@ -1836,7 +1836,7 @@
         "category": "function pointer",
         "args": [
             {"name": "type", "type": "error type"},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"},
+            {"name": "message", "type": "string view"},
             {"name": "userdata", "type": "void *"}
         ]
     },
@@ -1845,7 +1845,7 @@
         "args": [
             {"name": "device", "type": "device", "annotation": "const*", "length": 1},
             {"name": "type", "type": "error type"},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"}
+            {"name": "message", "type": "string view"}
         ]
     },
     "uncaptured error callback info": {
@@ -1875,7 +1875,7 @@
         "args": [
             {"name": "status", "type": "pop error scope status"},
             {"name": "type", "type": "error type"},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"},
+            {"name": "message", "type": "string view"},
             {"name": "userdata", "type": "void *"}
         ]
     },
@@ -1884,7 +1884,7 @@
         "args": [
             {"name": "status", "type": "pop error scope status"},
             {"name": "type", "type": "error type"},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"}
+            {"name": "message", "type": "string view"}
         ]
     },
     "pop error scope callback info": {
@@ -1979,7 +1979,7 @@
         "tags": ["dawn"],
         "args": [
             {"name": "type", "type": "logging type"},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"},
+            {"name": "message", "type": "string view"},
             {"name": "userdata", "type": "void *"}
         ]
     },
@@ -3701,7 +3701,7 @@
         "args": [
             {"name": "status", "type": "request device status"},
             {"name": "device", "type": "device", "optional": true},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
+            {"name": "message", "type": "string view"},
             {"name": "userdata", "type": "void *"}
         ]
     },
@@ -3711,7 +3711,7 @@
         "args": [
             {"name": "status", "type": "request device status"},
             {"name": "device", "type": "device", "optional": true},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}
+            {"name": "message", "type": "string view"}
         ]
     },
     "request device callback info": {
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index 3ca1755..270bb10 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -134,7 +134,7 @@
             { "name": "future", "type": "future" },
             { "name": "status", "type": "buffer map async status" },
             { "name": "status2", "type": "map async status" },
-            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true },
+            { "name": "message", "type": "string view" },
             { "name": "userdata count", "type": "uint8_t", "_comment": "TODO(crbug.com/dawn/2509): Remove this once Chromium overrides the correct functions in the proc table."},
             { "name": "read data update info length", "type": "uint64_t" },
             { "name": "read data update info", "type": "uint8_t", "annotation": "const*", "length": "read data update info length", "skip_serialize": true }
@@ -143,35 +143,35 @@
             { "name": "event manager", "type": "ObjectHandle" },
             { "name": "future", "type": "future" },
             { "name": "status", "type": "create pipeline async status" },
-            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
+            { "name": "message", "type": "string view"}
         ],
         "device create render pipeline async callback": [
             { "name": "event manager", "type": "ObjectHandle" },
             { "name": "future", "type": "future" },
             { "name": "status", "type": "create pipeline async status" },
-            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
+            { "name": "message", "type": "string view"}
         ],
         "device uncaptured error callback": [
             { "name": "device", "type": "ObjectHandle", "handle_type": "device" },
             { "name": "type", "type": "error type"},
-            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
+            { "name": "message", "type": "string view"}
         ],
         "device logging callback": [
             { "name": "device", "type": "ObjectHandle", "handle_type": "device" },
             { "name": "type", "type": "logging type"},
-            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
+            { "name": "message", "type": "string view" }
         ],
         "device lost callback" : [
             { "name": "event manager", "type": "ObjectHandle" },
             { "name": "future", "type": "future" },
             { "name": "reason", "type": "device lost reason" },
-            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
+            { "name": "message", "type": "string view"}
         ],
         "device pop error scope callback": [
             { "name": "event manager", "type": "ObjectHandle" },
             { "name": "future", "type": "future" },
             { "name": "type", "type": "error type" },
-            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
+            { "name": "message", "type": "string view"}
         ],
         "queue work done callback": [
             { "name": "event manager", "type": "ObjectHandle" },
@@ -188,7 +188,7 @@
             { "name": "event manager", "type": "ObjectHandle" },
             { "name": "future", "type": "future" },
             { "name": "status", "type": "request adapter status" },
-            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true },
+            { "name": "message", "type": "string view" },
             { "name": "info", "type": "adapter info", "annotation": "const*", "optional": "true" },
             { "name": "limits", "type": "supported limits", "annotation": "const*", "optional": "true" },
             { "name": "features count", "type": "uint32_t"},
@@ -198,7 +198,7 @@
             { "name": "event manager", "type": "ObjectHandle" },
             { "name": "future", "type": "future" },
             { "name": "status", "type": "request device status" },
-            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true },
+            { "name": "message", "type": "string view" },
             { "name": "limits", "type": "supported limits", "annotation": "const*", "optional": "true" },
             { "name": "features count", "type": "uint32_t"},
             { "name": "features", "type": "feature name", "annotation": "const*", "length": "features count"}
diff --git a/src/dawn/fuzzers/DawnWireServerFuzzer.cpp b/src/dawn/fuzzers/DawnWireServerFuzzer.cpp
index 5f8c19d..cad2158 100644
--- a/src/dawn/fuzzers/DawnWireServerFuzzer.cpp
+++ b/src/dawn/fuzzers/DawnWireServerFuzzer.cpp
@@ -36,6 +36,7 @@
 #include "dawn/common/Assert.h"
 #include "dawn/common/DynamicLib.h"
 #include "dawn/common/Log.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/common/SystemUtils.h"
 #include "dawn/dawn_proc.h"
 #include "dawn/native/DawnNative.h"
@@ -123,19 +124,20 @@
             if (sAdapterSupported(adapter)) {
                 WGPUAdapter cAdapter = adapter.Get();
                 dawn::native::GetProcs().adapterAddRef(cAdapter);
-                callback(WGPURequestAdapterStatus_Success, cAdapter, nullptr, userdata1, userdata2);
+                callback(WGPURequestAdapterStatus_Success, cAdapter, dawn::kEmptyOutputStringView,
+                         userdata1, userdata2);
                 return {};
             }
         }
-        callback(WGPURequestAdapterStatus_Unavailable, nullptr, "No supported adapter.", userdata1,
-                 userdata2);
+        callback(WGPURequestAdapterStatus_Unavailable, nullptr,
+                 dawn::ToOutputStringView("No supported adapter."), userdata1, userdata2);
         return {};
     };
     procs.instanceRequestAdapter = [](WGPUInstance cInstance, const WGPURequestAdapterOptions*,
                                       WGPURequestAdapterCallback callback, void* userdata) {
         RequestAdapter(
             cInstance,
-            [](WGPURequestAdapterStatus status, WGPUAdapter adapter, const char* message,
+            [](WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message,
                void* callback, void* userdata) {
                 auto cb = reinterpret_cast<WGPURequestAdapterCallback>(callback);
                 cb(status, adapter, message, userdata);
@@ -146,7 +148,7 @@
                                        WGPURequestAdapterCallbackInfo callbackInfo) -> WGPUFuture {
         return RequestAdapter(
             cInstance,
-            [](WGPURequestAdapterStatus status, WGPUAdapter adapter, const char* message,
+            [](WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message,
                void* callback, void* userdata) {
                 auto cb = reinterpret_cast<WGPURequestAdapterCallback>(callback);
                 cb(status, adapter, message, userdata);
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 3e79c840..57958d6 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -35,7 +35,7 @@
 #include <utility>
 #include <vector>
 
-#include "dawn/common/Log.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/native/ChainUtils.h"
 #include "dawn/native/Device.h"
 #include "dawn/native/Instance.h"
@@ -353,7 +353,7 @@
                                       const RequestDeviceCallbackInfo& callbackInfo) {
     return APIRequestDevice2(
         descriptor, {ToAPI(callbackInfo.nextInChain), ToAPI(callbackInfo.mode),
-                     [](WGPURequestDeviceStatus status, WGPUDevice device, char const* message,
+                     [](WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message,
                         void* callback, void* userdata) {
                          auto cb = reinterpret_cast<WGPURequestDeviceCallback>(callback);
                          cb(status, device, message, userdata);
@@ -400,9 +400,8 @@
                 mDevice = nullptr;
                 mMessage = "A valid external Instance reference no longer exists.";
             }
-            mCallback(mStatus, ToAPI(ReturnToAPI(std::move(mDevice))),
-                      mMessage.empty() ? nullptr : mMessage.c_str(), mUserdata1.ExtractAsDangling(),
-                      mUserdata2.ExtractAsDangling());
+            mCallback(mStatus, ToAPI(ReturnToAPI(std::move(mDevice))), ToOutputStringView(mMessage),
+                      mUserdata1.ExtractAsDangling(), mUserdata2.ExtractAsDangling());
         }
     };
 
diff --git a/src/dawn/native/Buffer.cpp b/src/dawn/native/Buffer.cpp
index 33bc88f..361b25a 100644
--- a/src/dawn/native/Buffer.cpp
+++ b/src/dawn/native/Buffer.cpp
@@ -37,6 +37,7 @@
 #include "absl/strings/str_format.h"
 #include "dawn/common/Alloc.h"
 #include "dawn/common/Assert.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/native/Adapter.h"
 #include "dawn/native/CallbackTaskManager.h"
 #include "dawn/native/ChainUtils.h"
@@ -340,8 +341,8 @@
 
         if (completionType == EventCompletionType::Shutdown) {
             mCallback(WGPUMapAsyncStatus_InstanceDropped,
-                      "A valid external Instance reference no longer exists.", userdata1,
-                      userdata2);
+                      ToOutputStringView("A valid external Instance reference no longer exists."),
+                      userdata1, userdata2);
             return;
         }
 
@@ -368,10 +369,10 @@
         });
         if (error) {
             DAWN_ASSERT(!pendingErrorData.message.empty());
-            mCallback(pendingErrorData.status, pendingErrorData.message.c_str(), userdata1,
-                      userdata2);
+            mCallback(pendingErrorData.status, ToOutputStringView(pendingErrorData.message),
+                      userdata1, userdata2);
         } else {
-            mCallback(WGPUMapAsyncStatus_Success, nullptr, userdata1, userdata2);
+            mCallback(WGPUMapAsyncStatus_Success, kEmptyOutputStringView, userdata1, userdata2);
         }
     }
 
diff --git a/src/dawn/native/CreatePipelineAsyncEvent.cpp b/src/dawn/native/CreatePipelineAsyncEvent.cpp
index 333a087..854c7ff 100644
--- a/src/dawn/native/CreatePipelineAsyncEvent.cpp
+++ b/src/dawn/native/CreatePipelineAsyncEvent.cpp
@@ -33,6 +33,7 @@
 
 #include "dawn/common/FutureUtils.h"
 #include "dawn/common/Ref.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/native/AsyncTask.h"
 #include "dawn/native/ComputePipeline.h"
 #include "dawn/native/Device.h"
@@ -186,8 +187,8 @@
 
     if (completionType == EventCompletionType::Shutdown) {
         if (mCallback) {
-            mCallback(WGPUCreatePipelineAsyncStatus_InstanceDropped, nullptr, "Instance dropped",
-                      userdata1, userdata2);
+            mCallback(WGPUCreatePipelineAsyncStatus_InstanceDropped, nullptr,
+                      ToOutputStringView("Instance dropped"), userdata1, userdata2);
         }
         return;
     }
@@ -202,7 +203,8 @@
         }
         if (mCallback) {
             mCallback(WGPUCreatePipelineAsyncStatus_Success,
-                      ToAPI(ReturnToAPI(std::move(mPipeline))), "", userdata1, userdata2);
+                      ToAPI(ReturnToAPI(std::move(mPipeline))), kEmptyOutputStringView, userdata1,
+                      userdata2);
         }
         return;
     }
@@ -218,7 +220,8 @@
                 break;
         }
         if (mCallback) {
-            mCallback(status, nullptr, mError->GetFormattedMessage().c_str(), userdata1, userdata2);
+            mCallback(status, nullptr, ToOutputStringView(mError->GetFormattedMessage()), userdata1,
+                      userdata2);
         }
         return;
     }
@@ -226,7 +229,7 @@
     AddOrGetCachedPipeline();
     if (mCallback) {
         mCallback(WGPUCreatePipelineAsyncStatus_Success, ToAPI(ReturnToAPI(std::move(mPipeline))),
-                  "", userdata1, userdata2);
+                  kEmptyOutputStringView, userdata1, userdata2);
     }
 }
 
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 343ef36..c41eb0e 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -38,6 +38,7 @@
 #include "absl/strings/str_format.h"
 #include "dawn/common/Log.h"
 #include "dawn/common/Ref.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/common/Version_autogen.h"
 #include "dawn/native/AsyncTask.h"
 #include "dawn/native/AttachmentState.h"
@@ -148,14 +149,16 @@
     }
 
   private:
-    void FinishImpl() override { mCallback(mLoggingType, mMessage.c_str(), mUserdata); }
+    void FinishImpl() override { mCallback(mLoggingType, ToOutputStringView(mMessage), mUserdata); }
 
     void HandleShutDownImpl() override {
         // Do the logging anyway
-        mCallback(mLoggingType, mMessage.c_str(), mUserdata);
+        mCallback(mLoggingType, ToOutputStringView(mMessage), mUserdata);
     }
 
-    void HandleDeviceLossImpl() override { mCallback(mLoggingType, mMessage.c_str(), mUserdata); }
+    void HandleDeviceLossImpl() override {
+        mCallback(mLoggingType, ToOutputStringView(mMessage), mUserdata);
+    }
 
     // As all deferred callback tasks will be triggered before modifying the registered
     // callback or shutting down, we are ensured that callback function and userdata pointer
@@ -168,7 +171,7 @@
 
 void LegacyDeviceLostCallback(WGPUDevice const* device,
                               WGPUDeviceLostReason reason,
-                              char const* message,
+                              WGPUStringView message,
                               void* callback,
                               void* userdata) {
     if (callback == nullptr) {
@@ -180,7 +183,7 @@
 
 void LegacyDeviceLostCallback2(WGPUDevice const* device,
                                WGPUDeviceLostReason reason,
-                               char const* message,
+                               WGPUStringView message,
                                void* callback,
                                void* userdata) {
     if (callback == nullptr) {
@@ -192,7 +195,7 @@
 
 void LegacyUncapturedErrorCallback(WGPUDevice const* device,
                                    WGPUErrorType type,
-                                   const char* message,
+                                   WGPUStringView message,
                                    void* callback,
                                    void* userdata) {
     if (callback == nullptr) {
@@ -227,7 +230,7 @@
     // TODO(crbug.com/dawn/2465) Make default AllowSpontaneous once SetDeviceLostCallback is gone.
     static constexpr WGPUDeviceLostCallbackInfo2 kDefaultDeviceLostCallbackInfo = {
         nullptr, WGPUCallbackMode_AllowProcessEvents,
-        [](WGPUDevice const*, WGPUDeviceLostReason, char const*, void*, void*) {
+        [](WGPUDevice const*, WGPUDeviceLostReason, WGPUStringView, void*, void*) {
             static bool calledOnce = false;
             if (!calledOnce) {
                 calledOnce = true;
@@ -278,7 +281,7 @@
         device = nullptr;
     }
     if (mCallback) {
-        mCallback(&device, ToAPI(mReason), mMessage.c_str(), userdata1, userdata2);
+        mCallback(&device, ToAPI(mReason), ToOutputStringView(mMessage), userdata1, userdata2);
     }
 
     // After the device lost callback fires, the uncaptured error callback is no longer valid so we
@@ -352,7 +355,7 @@
 #if defined(DAWN_ENABLE_ASSERTS)
     static constexpr WGPUUncapturedErrorCallbackInfo2 kDefaultUncapturedErrorCallbackInfo = {
         nullptr,
-        [](WGPUDevice const*, WGPUErrorType, char const*, void*, void*) {
+        [](WGPUDevice const*, WGPUErrorType, WGPUStringView, void*, void*) {
             static bool calledOnce = false;
             if (!calledOnce) {
                 calledOnce = true;
@@ -791,7 +794,7 @@
             if (mUncapturedErrorCallbackInfo.callback != nullptr && mState == State::Alive) {
                 auto device = ToAPI(this);
                 mUncapturedErrorCallbackInfo.callback(
-                    &device, ToAPI(ToWGPUErrorType(type)), messageStr.c_str(),
+                    &device, ToAPI(ToWGPUErrorType(type)), ToOutputStringView(messageStr),
                     mUncapturedErrorCallbackInfo.userdata1, mUncapturedErrorCallbackInfo.userdata2);
             }
         }
@@ -870,10 +873,10 @@
 }
 
 void DeviceBase::APIPopErrorScope(wgpu::ErrorCallback callback, void* userdata) {
-    static wgpu::ErrorCallback kDefaultCallback = [](WGPUErrorType, char const*, void*) {};
+    static wgpu::ErrorCallback kDefaultCallback = [](WGPUErrorType, WGPUStringView, void*) {};
 
     APIPopErrorScope2({nullptr, WGPUCallbackMode_AllowProcessEvents,
-                       [](WGPUPopErrorScopeStatus, WGPUErrorType type, char const* message,
+                       [](WGPUPopErrorScopeStatus, WGPUErrorType type, WGPUStringView message,
                           void* callback, void* userdata) {
                            auto cb = reinterpret_cast<wgpu::ErrorCallback>(callback);
                            cb(type, message, userdata);
@@ -885,7 +888,7 @@
 Future DeviceBase::APIPopErrorScopeF(const PopErrorScopeCallbackInfo& callbackInfo) {
     return APIPopErrorScope2({ToAPI(callbackInfo.nextInChain), ToAPI(callbackInfo.mode),
                               [](WGPUPopErrorScopeStatus status, WGPUErrorType type,
-                                 char const* message, void* callback, void* userdata) {
+                                 WGPUStringView message, void* callback, void* userdata) {
                                   auto cb = reinterpret_cast<WGPUPopErrorScopeCallback>(callback);
                                   cb(status, type, message, userdata);
                               },
@@ -916,13 +919,13 @@
                                                  ? WGPUPopErrorScopeStatus_Success
                                                  : WGPUPopErrorScopeStatus_InstanceDropped;
             WGPUErrorType type;
-            const char* message;
+            WGPUStringView message;
             if (mScope) {
                 type = static_cast<WGPUErrorType>(mScope->GetErrorType());
-                message = mScope->GetErrorMessage().c_str();
+                message = ToOutputStringView(mScope->GetErrorMessage());
             } else {
                 type = WGPUErrorType_Unknown;
-                message = "No error scopes to pop";
+                message = ToOutputStringView("No error scopes to pop");
             }
 
             mCallback(status, type, message, mUserdata1.ExtractAsDangling(),
@@ -1377,7 +1380,7 @@
     APICreateComputePipelineAsync2(
         descriptor, {nullptr, WGPUCallbackMode_AllowProcessEvents,
                      [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline,
-                        char const* message, void* callback, void* userdata) {
+                        WGPUStringView message, void* callback, void* userdata) {
                          auto cb =
                              reinterpret_cast<WGPUCreateComputePipelineAsyncCallback>(callback);
                          cb(status, pipeline, message, userdata);
@@ -1393,7 +1396,7 @@
     return APICreateComputePipelineAsync2(
         descriptor, {ToAPI(callbackInfo.nextInChain), ToAPI(callbackInfo.mode),
                      [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline,
-                        char const* message, void* callback, void* userdata) {
+                        WGPUStringView message, void* callback, void* userdata) {
                          auto cb =
                              reinterpret_cast<WGPUCreateComputePipelineAsyncCallback>(callback);
                          cb(status, pipeline, message, userdata);
@@ -1479,7 +1482,7 @@
     APICreateRenderPipelineAsync2(
         descriptor, {nullptr, WGPUCallbackMode_AllowProcessEvents,
                      [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline,
-                        char const* message, void* callback, void* userdata) {
+                        WGPUStringView message, void* callback, void* userdata) {
                          auto cb =
                              reinterpret_cast<WGPUCreateRenderPipelineAsyncCallback>(callback);
                          cb(status, pipeline, message, userdata);
@@ -1495,7 +1498,7 @@
     return APICreateRenderPipelineAsync2(
         descriptor, {ToAPI(callbackInfo.nextInChain), ToAPI(callbackInfo.mode),
                      [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline,
-                        char const* message, void* callback, void* userdata) {
+                        WGPUStringView message, void* callback, void* userdata) {
                          auto cb =
                              reinterpret_cast<WGPUCreateRenderPipelineAsyncCallback>(callback);
                          cb(status, pipeline, message, userdata);
@@ -1920,18 +1923,18 @@
     EmitLog(WGPULoggingType_Warning, t.str().c_str());
 }
 
-void DeviceBase::EmitLog(const char* message) {
+void DeviceBase::EmitLog(std::string_view message) {
     this->EmitLog(WGPULoggingType_Info, message);
 }
 
-void DeviceBase::EmitLog(WGPULoggingType loggingType, const char* message) {
+void DeviceBase::EmitLog(WGPULoggingType loggingType, std::string_view message) {
     // Acquire a shared lock. This allows multiple threads to emit logs,
     // or even logs to be emitted re-entrantly. It will block if there is a call
     // to SetLoggingCallback. Applications should not call SetLoggingCallback inside
     // the logging callback or they will deadlock.
     std::shared_lock<std::shared_mutex> lock(mLoggingMutex);
     if (mLoggingCallback) {
-        mLoggingCallback(loggingType, message, mLoggingUserdata);
+        mLoggingCallback(loggingType, ToOutputStringView(message), mLoggingUserdata);
     }
 }
 
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index 17d5c6e..dd88465 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -364,8 +364,8 @@
     size_t GetLazyClearCountForTesting();
     void IncrementLazyClearCountForTesting();
     void EmitWarningOnce(const std::string& message);
-    void EmitLog(const char* message);
-    void EmitLog(WGPULoggingType loggingType, const char* message);
+    void EmitLog(std::string_view message);
+    void EmitLog(WGPULoggingType loggingType, std::string_view message);
     void EmitCompilationLog(const ShaderModuleBase* module);
     void APIForceLoss(wgpu::DeviceLostReason reason, const char* message) {
         // TODO(crbug.com/42241188): Remove const char* version of the method.
diff --git a/src/dawn/native/Instance.cpp b/src/dawn/native/Instance.cpp
index 61ababb..5eadadf 100644
--- a/src/dawn/native/Instance.cpp
+++ b/src/dawn/native/Instance.cpp
@@ -33,6 +33,7 @@
 #include "dawn/common/FutureUtils.h"
 #include "dawn/common/GPUInfo.h"
 #include "dawn/common/Log.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/common/SystemUtils.h"
 #include "dawn/common/WGSLFeatureMapping.h"
 #include "dawn/native/CallbackTaskManager.h"
@@ -226,19 +227,20 @@
     }
 
     if (!mLoggingCallback) {
-        mLoggingCallback = [](WGPULoggingType type, char const* message, void*) {
+        mLoggingCallback = [](WGPULoggingType type, WGPUStringView message, void*) {
+            std::string_view view = {message.data, message.length};
             switch (static_cast<wgpu::LoggingType>(type)) {
                 case wgpu::LoggingType::Verbose:
-                    dawn::DebugLog() << message;
+                    dawn::DebugLog() << view;
                     break;
                 case wgpu::LoggingType::Info:
-                    dawn::InfoLog() << message;
+                    dawn::InfoLog() << view;
                     break;
                 case wgpu::LoggingType::Warning:
-                    dawn::WarningLog() << message;
+                    dawn::WarningLog() << view;
                     break;
                 case wgpu::LoggingType::Error:
-                    dawn::ErrorLog() << message;
+                    dawn::ErrorLog() << view;
                     break;
             }
         };
@@ -274,7 +276,7 @@
                                         const RequestAdapterCallbackInfo& callbackInfo) {
     return APIRequestAdapter2(
         options, {ToAPI(callbackInfo.nextInChain), ToAPI(callbackInfo.mode),
-                  [](WGPURequestAdapterStatus status, WGPUAdapter adapter, char const* message,
+                  [](WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message,
                      void* callback, void* userdata) {
                       auto cb = reinterpret_cast<WGPURequestAdapterCallback>(callback);
                       cb(status, adapter, message, userdata);
@@ -303,17 +305,18 @@
 
         void Complete(EventCompletionType completionType) override {
             if (completionType == EventCompletionType::Shutdown) {
-                mCallback(WGPURequestAdapterStatus_InstanceDropped, nullptr, nullptr,
+                mCallback(WGPURequestAdapterStatus_InstanceDropped, nullptr, kEmptyOutputStringView,
                           mUserdata1.ExtractAsDangling(), mUserdata2.ExtractAsDangling());
                 return;
             }
 
             WGPUAdapter adapter = ToAPI(ReturnToAPI(std::move(mAdapter)));
             if (adapter == nullptr) {
-                mCallback(WGPURequestAdapterStatus_Unavailable, nullptr, "No supported adapters",
+                mCallback(WGPURequestAdapterStatus_Unavailable, nullptr,
+                          ToOutputStringView("No supported adapters"),
                           mUserdata1.ExtractAsDangling(), mUserdata2.ExtractAsDangling());
             } else {
-                mCallback(WGPURequestAdapterStatus_Success, adapter, nullptr,
+                mCallback(WGPURequestAdapterStatus_Success, adapter, kEmptyOutputStringView,
                           mUserdata1.ExtractAsDangling(), mUserdata2.ExtractAsDangling());
             }
         }
@@ -489,7 +492,8 @@
     }
     std::string message = maybeErr.AcquireError()->GetFormattedMessage();
     if (mWarningMessages.insert(message).second && mLoggingCallback) {
-        mLoggingCallback(WGPULoggingType_Warning, message.c_str(), mLoggingCallbackUserdata);
+        mLoggingCallback(WGPULoggingType_Warning, ToOutputStringView(message),
+                         mLoggingCallbackUserdata);
     }
     return true;
 }
@@ -596,7 +600,8 @@
     DAWN_ASSERT(error != nullptr);
     if (mLoggingCallback) {
         std::string messageStr = error->GetFormattedMessage();
-        mLoggingCallback(WGPULoggingType_Error, messageStr.c_str(), mLoggingCallbackUserdata);
+        mLoggingCallback(WGPULoggingType_Error, ToOutputStringView(messageStr),
+                         mLoggingCallbackUserdata);
     }
 }
 
diff --git a/src/dawn/node/binding/GPUDevice.cpp b/src/dawn/node/binding/GPUDevice.cpp
index 98ce43e..a41476e 100644
--- a/src/dawn/node/binding/GPUDevice.cpp
+++ b/src/dawn/node/binding/GPUDevice.cpp
@@ -75,13 +75,16 @@
 // There's something broken with Node when attempting to write more than 65536 bytes to cout.
 // Split the string up into writes of 4k chunks.
 // Likely related: https://github.com/nodejs/node/issues/12921
-void chunkedWrite(const char* msg) {
-    while (true) {
-        auto n = printf("%.4096s", msg);
-        if (n <= 0) {
-            break;
+void chunkedWrite(WGPUStringView msg) {
+    while (msg.length != 0) {
+        int n;
+        if (msg.length > 4096) {
+            n = printf("%.4096s", msg.data);
+        } else {
+            n = printf("%.*s", static_cast<int>(msg.length), msg.data);
         }
-        msg += n;
+        msg.data += n;
+        msg.length -= n;
     }
 }
 
@@ -145,7 +148,7 @@
       lost_promise_(lost_promise),
       label_(CopyLabel(desc.label)) {
     device_.SetLoggingCallback(
-        [](WGPULoggingType type, char const* message, void* userdata) {
+        [](WGPULoggingType type, WGPUStringView message, void* userdata) {
             printf("%s:\n", str(type));
             chunkedWrite(message);
         },
diff --git a/src/dawn/samples/ManualSurfaceTest.cpp b/src/dawn/samples/ManualSurfaceTest.cpp
index ec71351..efc460f 100644
--- a/src/dawn/samples/ManualSurfaceTest.cpp
+++ b/src/dawn/samples/ManualSurfaceTest.cpp
@@ -440,9 +440,16 @@
     std::cout << "Using adapter \"" << adapterInfo.device << "\" on " << adapterInfo.backendType
               << ".\n";
 
+    wgpu::DeviceDescriptor deviceDesc;
+    deviceDesc.SetUncapturedErrorCallback(
+        [](const wgpu::Device&, wgpu::ErrorType errorType, const char* message) {
+            dawn::ErrorLog() << errorType << " error: " << message;
+            DAWN_ASSERT(false);
+        });
+
     // Setup the device on that adapter.
     wgpu::Future deviceFuture = adapter.RequestDevice(
-        nullptr, wgpu::CallbackMode::WaitAnyOnly,
+        &deviceDesc, wgpu::CallbackMode::WaitAnyOnly,
         [&](wgpu::RequestDeviceStatus status, wgpu::Device deviceIn, const char* message) {
             if (status != wgpu::RequestDeviceStatus::Success) {
                 dawn::ErrorLog() << "Failed to get a device:" << message;
@@ -456,30 +463,6 @@
         return 1;
     }
 
-    device.SetUncapturedErrorCallback(
-        [](WGPUErrorType errorType, const char* message, void*) {
-            const char* errorTypeName = "";
-            switch (errorType) {
-                case WGPUErrorType_Validation:
-                    errorTypeName = "Validation";
-                    break;
-                case WGPUErrorType_OutOfMemory:
-                    errorTypeName = "Out of memory";
-                    break;
-                case WGPUErrorType_Unknown:
-                    errorTypeName = "Unknown";
-                    break;
-                case WGPUErrorType_DeviceLost:
-                    errorTypeName = "Device lost";
-                    break;
-                default:
-                    DAWN_UNREACHABLE();
-                    return;
-            }
-            dawn::ErrorLog() << errorTypeName << " error: " << message;
-            DAWN_ASSERT(false);
-        },
-        nullptr);
     queue = device.GetQueue();
 
     // Create the first window, since the example exits when there are no windows.
diff --git a/src/dawn/samples/SampleUtils.cpp b/src/dawn/samples/SampleUtils.cpp
index cb6095c..ddafcbf 100644
--- a/src/dawn/samples/SampleUtils.cpp
+++ b/src/dawn/samples/SampleUtils.cpp
@@ -198,7 +198,7 @@
     wgpu::DeviceDescriptor deviceDesc = {};
     deviceDesc.SetDeviceLostCallback(
         wgpu::CallbackMode::AllowSpontaneous,
-        [](const wgpu::Device&, wgpu::DeviceLostReason reason, const char* message) {
+        [](const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView message) {
             const char* reasonName = "";
             switch (reason) {
                 case wgpu::DeviceLostReason::Unknown:
@@ -219,7 +219,7 @@
             dawn::ErrorLog() << "Device lost because of " << reasonName << ": " << message;
         });
     deviceDesc.SetUncapturedErrorCallback(
-        [](const wgpu::Device&, wgpu::ErrorType type, const char* message) {
+        [](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView message) {
             const char* errorTypeName = "";
             switch (type) {
                 case wgpu::ErrorType::Validation:
@@ -244,7 +244,7 @@
     sample->instance.WaitAny(
         sample->adapter.RequestDevice(
             &deviceDesc, wgpu::CallbackMode::WaitAnyOnly,
-            [](wgpu::RequestDeviceStatus status, wgpu::Device device, const char* message) {
+            [](wgpu::RequestDeviceStatus status, wgpu::Device device, wgpu::StringView message) {
                 if (status != wgpu::RequestDeviceStatus::Success) {
                     dawn::ErrorLog() << "Failed to get an device:" << message;
                     return;
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index e65daa9..b88aa55 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -316,6 +316,7 @@
     "DawnNativeTest.h",
     "MockCallback.h",
     "ParamGenerator.h",
+    "StringViewMatchers.h",
     "ToggleParser.cpp",
     "ToggleParser.h",
     "unittests/AsyncTaskTests.cpp",
@@ -530,6 +531,7 @@
     "DawnTest.h",
     "MockCallback.h",
     "ParamGenerator.h",
+    "StringViewMatchers.h",
     "ToggleParser.cpp",
     "ToggleParser.h",
   ]
diff --git a/src/dawn/tests/DawnNativeTest.cpp b/src/dawn/tests/DawnNativeTest.cpp
index 89b69cf..517b481 100644
--- a/src/dawn/tests/DawnNativeTest.cpp
+++ b/src/dawn/tests/DawnNativeTest.cpp
@@ -36,6 +36,7 @@
 #include "dawn/native/Instance.h"
 #include "dawn/native/dawn_platform.h"
 #include "dawn/platform/DawnPlatform.h"
+#include "dawn/webgpu_cpp_print.h"
 
 namespace dawn::native {
 
@@ -90,7 +91,7 @@
 WGPUDevice DawnNativeTest::CreateTestDevice() {
     wgpu::DeviceDescriptor desc = {};
     desc.SetUncapturedErrorCallback(
-        [](const wgpu::Device&, wgpu::ErrorType type, const char* message) {
+        [](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView message) {
             DAWN_ASSERT(type != wgpu::ErrorType::NoError);
             FAIL() << "Unexpected error: " << message;
         });
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp
index 6ba7dd0..3d84fe2 100644
--- a/src/dawn/tests/DawnTest.cpp
+++ b/src/dawn/tests/DawnTest.cpp
@@ -43,6 +43,7 @@
 #include "dawn/common/Log.h"
 #include "dawn/common/Math.h"
 #include "dawn/common/Platform.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/common/SystemUtils.h"
 #include "dawn/dawn_proc.h"
 #include "dawn/native/Device.h"
@@ -754,7 +755,7 @@
         WGPUAdapter cAdapter = it->Get();
         DAWN_ASSERT(cAdapter);
         native::GetProcs().adapterAddRef(cAdapter);
-        callbackInfo.callback(WGPURequestAdapterStatus_Success, cAdapter, nullptr,
+        callbackInfo.callback(WGPURequestAdapterStatus_Success, cAdapter, kEmptyOutputStringView,
                               callbackInfo.userdata1, callbackInfo.userdata2);
 
         // Returning a placeholder future that we should never be waiting on.
@@ -778,7 +779,7 @@
         DAWN_ASSERT(cDevice != nullptr);
 
         gCurrentTest->mLastCreatedBackendDevice = cDevice;
-        callbackInfo.callback(WGPURequestDeviceStatus_Success, cDevice, nullptr,
+        callbackInfo.callback(WGPURequestDeviceStatus_Success, cDevice, kEmptyOutputStringView,
                               callbackInfo.userdata1, callbackInfo.userdata2);
 
         // Returning a placeholder future that we should never be waiting on.
@@ -1190,19 +1191,20 @@
         .Times(AtMost(1));
 
     apiDevice.SetLoggingCallback(
-        [](WGPULoggingType type, char const* message, void*) {
+        [](WGPULoggingType type, WGPUStringView message, void*) {
+            std::string_view view = {message.data, message.length};
             switch (type) {
                 case WGPULoggingType_Verbose:
-                    DebugLog() << message;
+                    DebugLog() << view;
                     break;
                 case WGPULoggingType_Warning:
-                    WarningLog() << message;
+                    WarningLog() << view;
                     break;
                 case WGPULoggingType_Error:
-                    ErrorLog() << message;
+                    ErrorLog() << view;
                     break;
                 default:
-                    InfoLog() << message;
+                    InfoLog() << view;
                     break;
             }
         },
diff --git a/src/dawn/tests/DawnTest.h b/src/dawn/tests/DawnTest.h
index 416b355..9c78dfd 100644
--- a/src/dawn/tests/DawnTest.h
+++ b/src/dawn/tests/DawnTest.h
@@ -344,10 +344,10 @@
     // device loss that aren't expected should result in test failures and not just some warnings
     // printed to stdout.
     testing::StrictMock<
-        testing::MockCppCallback<void (*)(const wgpu::Device&, wgpu::ErrorType, const char*)>>
+        testing::MockCppCallback<void (*)(const wgpu::Device&, wgpu::ErrorType, wgpu::StringView)>>
         mDeviceErrorCallback;
     testing::StrictMock<testing::MockCppCallback<
-        void (*)(const wgpu::Device&, wgpu::DeviceLostReason, const char*)>>
+        void (*)(const wgpu::Device&, wgpu::DeviceLostReason, wgpu::StringView)>>
         mDeviceLostCallback;
 
     // Helper methods to implement the EXPECT_ macros
diff --git a/src/dawn/tests/StringViewMatchers.h b/src/dawn/tests/StringViewMatchers.h
new file mode 100644
index 0000000..a51912c
--- /dev/null
+++ b/src/dawn/tests/StringViewMatchers.h
@@ -0,0 +1,60 @@
+// Copyright 2020 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_TESTS_STRINGVIEWMATCHERS_H_
+#define SRC_DAWN_TESTS_STRINGVIEWMATCHERS_H_
+
+#include <string_view>
+
+#include "dawn/webgpu_cpp.h"
+#include "gmock/gmock.h"
+
+namespace testing {
+
+MATCHER(EmptySizedString, "") {
+    return arg.length == 0;
+}
+
+MATCHER(NonEmptySizedString, "") {
+    return arg.length != 0 && arg.length != WGPU_STRLEN;
+}
+
+MATCHER_P(SizedString, expected, "") {
+    return arg.length != WGPU_STRLEN && std::string_view(arg.data, arg.length) == expected;
+}
+
+MATCHER_P(SizedStringMatches, matcher, "") {
+    if (arg.length == WGPU_STRLEN) {
+        return false;
+    }
+    std::string_view v = {arg.data, arg.length};
+    return Matches(matcher)(v);
+}
+
+}  // namespace testing
+
+#endif  // SRC_DAWN_TESTS_STRINGVIEWMATCHERS_H_
diff --git a/src/dawn/tests/benchmarks/NullDeviceSetup.cpp b/src/dawn/tests/benchmarks/NullDeviceSetup.cpp
index 72630fe..656c38e 100644
--- a/src/dawn/tests/benchmarks/NullDeviceSetup.cpp
+++ b/src/dawn/tests/benchmarks/NullDeviceSetup.cpp
@@ -29,6 +29,7 @@
 
 #include <benchmark/benchmark.h>
 #include <dawn/webgpu_cpp.h>
+#include <dawn/webgpu_cpp_print.h>
 #include <memory>
 #include <utility>
 
@@ -62,14 +63,14 @@
             wgpu::DeviceDescriptor desc = GetDeviceDescriptor();
             desc.SetDeviceLostCallback(
                 wgpu::CallbackMode::AllowSpontaneous,
-                [](const wgpu::Device&, wgpu::DeviceLostReason reason, char const* message) {
+                [](const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView message) {
                     if (reason == wgpu::DeviceLostReason::Unknown) {
                         dawn::ErrorLog() << message;
                         DAWN_UNREACHABLE();
                     }
                 });
             desc.SetUncapturedErrorCallback(
-                [](const wgpu::Device&, wgpu::ErrorType, const char* message) {
+                [](const wgpu::Device&, wgpu::ErrorType, wgpu::StringView message) {
                     dawn::ErrorLog() << message;
                     DAWN_UNREACHABLE();
                 });
diff --git a/src/dawn/tests/end2end/AdapterCreationTests.cpp b/src/dawn/tests/end2end/AdapterCreationTests.cpp
index b0d23c0..12dff71 100644
--- a/src/dawn/tests/end2end/AdapterCreationTests.cpp
+++ b/src/dawn/tests/end2end/AdapterCreationTests.cpp
@@ -38,12 +38,14 @@
 #include "dawn/native/DawnNative.h"
 #include "dawn/tests/DawnTest.h"
 #include "dawn/tests/MockCallback.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "gtest/gtest.h"
 
 namespace dawn {
 namespace {
 
 using testing::_;
+using testing::EmptySizedString;
 using testing::MockCallback;
 using testing::SaveArg;
 
@@ -129,7 +131,7 @@
     MockCallback<WGPURequestAdapterCallback> cb;
 
     WGPUAdapter cAdapter = nullptr;
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
         .WillOnce(SaveArg<1>(&cAdapter));
     RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
@@ -144,14 +146,14 @@
     MockCallback<WGPURequestAdapterCallback> cb;
 
     WGPUAdapter cAdapter = nullptr;
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
         .WillOnce(SaveArg<1>(&cAdapter));
     RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
 
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this + 1))
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this + 1))
         .WillOnce(SaveArg<1>(&cAdapter));
     RequestAdapter(instance, nullptr, cb.Callback(), cb.MakeUserdata(this + 1));
 
@@ -168,7 +170,7 @@
 
     WGPUAdapter cAdapter = nullptr;
     if (swiftShaderAvailable) {
-        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
             .WillOnce(SaveArg<1>(&cAdapter));
     } else {
         EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unavailable, nullptr, _, this))
@@ -196,7 +198,7 @@
 
     WGPUAdapter cAdapter = nullptr;
     if (anyAdapterAvailable) {
-        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
             .WillOnce(SaveArg<1>(&cAdapter));
     } else {
         EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unavailable, nullptr, _, this))
@@ -225,7 +227,7 @@
 
     WGPUAdapter cAdapter = nullptr;
     if (anyAdapterAvailable) {
-        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
             .WillOnce(SaveArg<1>(&cAdapter));
     } else {
         EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unavailable, nullptr, _, this))
@@ -253,7 +255,7 @@
     MockCallback<WGPURequestAdapterCallback> cb;
 
     WGPUAdapter cAdapter = nullptr;
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
         .WillOnce(SaveArg<1>(&cAdapter));
     RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
@@ -272,7 +274,7 @@
     MockCallback<WGPURequestAdapterCallback> cb;
 
     WGPUAdapter cAdapter = nullptr;
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
         .WillOnce(SaveArg<1>(&cAdapter));
     RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
@@ -291,7 +293,7 @@
     MockCallback<WGPURequestAdapterCallback> cb;
 
     WGPUAdapter cAdapter = nullptr;
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
         .WillOnce(SaveArg<1>(&cAdapter));
     RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
@@ -309,7 +311,7 @@
     MockCallback<WGPURequestAdapterCallback> cb;
 
     WGPUAdapter cAdapter = nullptr;
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
         .WillOnce(SaveArg<1>(&cAdapter));
     RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
@@ -341,7 +343,7 @@
     MockCallback<WGPURequestAdapterCallback> cb;
 
     WGPUAdapter cAdapter = nullptr;
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
         .WillOnce(SaveArg<1>(&cAdapter));
     RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
@@ -402,7 +404,7 @@
     MockCallback<WGPURequestAdapterCallback> cb;
 
     WGPUAdapter cAdapter = nullptr;
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
         .WillOnce(SaveArg<1>(&cAdapter));
     RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
@@ -461,7 +463,7 @@
     MockCallback<WGPURequestAdapterCallback> cb;
 
     WGPUAdapter cAdapter = nullptr;
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, EmptySizedString(), this))
         .WillOnce(SaveArg<1>(&cAdapter));
     RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
diff --git a/src/dawn/tests/end2end/EventTests.cpp b/src/dawn/tests/end2end/EventTests.cpp
index 02d197f..065f514 100644
--- a/src/dawn/tests/end2end/EventTests.cpp
+++ b/src/dawn/tests/end2end/EventTests.cpp
@@ -53,7 +53,7 @@
     wgpu::Adapter adapter2;
     requestAdapter(
         instance.Get(), nullptr,
-        [](WGPURequestAdapterStatus status, WGPUAdapter adapter, const char*, void* userdata) {
+        [](WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView, void* userdata) {
             ASSERT_EQ(status, WGPURequestAdapterStatus_Success);
             *reinterpret_cast<wgpu::Adapter*>(userdata) = wgpu::Adapter::Acquire(adapter);
         },
@@ -63,7 +63,7 @@
     wgpu::Device device2;
     requestDevice(
         adapter2.Get(), nullptr,
-        [](WGPURequestDeviceStatus status, WGPUDevice device, const char*, void* userdata) {
+        [](WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView, void* userdata) {
             ASSERT_EQ(status, WGPURequestDeviceStatus_Success);
             *reinterpret_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(device);
         },
diff --git a/src/dawn/tests/unittests/native/AllowedErrorTests.cpp b/src/dawn/tests/unittests/native/AllowedErrorTests.cpp
index 3db6073..34d8f7c 100644
--- a/src/dawn/tests/unittests/native/AllowedErrorTests.cpp
+++ b/src/dawn/tests/unittests/native/AllowedErrorTests.cpp
@@ -33,6 +33,7 @@
 
 #include "dawn/native/ChainUtils.h"
 #include "dawn/tests/MockCallback.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "mocks/BufferMock.h"
 #include "mocks/ComputePipelineMock.h"
 #include "mocks/DawnMockTest.h"
@@ -53,13 +54,14 @@
 using ::testing::MockCppCallback;
 using ::testing::NiceMock;
 using ::testing::Return;
+using ::testing::SizedStringMatches;
 using ::testing::StrictMock;
 using ::testing::Test;
 
-using MockComputePipelineAsyncCallback =
-    MockCppCallback<void (*)(wgpu::CreatePipelineAsyncStatus, wgpu::ComputePipeline, const char*)>;
-using MockRenderPipelineAsyncCallback =
-    MockCppCallback<void (*)(wgpu::CreatePipelineAsyncStatus, wgpu::RenderPipeline, const char*)>;
+using MockComputePipelineAsyncCallback = MockCppCallback<
+    void (*)(wgpu::CreatePipelineAsyncStatus, wgpu::ComputePipeline, wgpu::StringView)>;
+using MockRenderPipelineAsyncCallback = MockCppCallback<
+    void (*)(wgpu::CreatePipelineAsyncStatus, wgpu::RenderPipeline, wgpu::StringView)>;
 
 static constexpr char kOomErrorMessage[] = "Out of memory error";
 static constexpr char kInternalErrorMessage[] = "Internal error";
@@ -100,8 +102,8 @@
         .WillOnce(Return(ByMove(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage))));
 
     // Expect the device lost because of the error.
-    EXPECT_CALL(mDeviceLostCb,
-                Call(WGPUDeviceLostReason_Unknown, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Unknown,
+                                    SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
 
     device.GetQueue().Submit(0, nullptr);
@@ -118,8 +120,8 @@
         .WillOnce(Return(ByMove(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage))));
 
     // Expect the device lost because of the error.
-    EXPECT_CALL(mDeviceLostCb,
-                Call(WGPUDeviceLostReason_Unknown, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Unknown,
+                                    SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
 
     constexpr uint8_t data = 8;
@@ -139,8 +141,8 @@
         .WillOnce(Return(ByMove(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage))));
 
     // Expect the device lost because of the error.
-    EXPECT_CALL(mDeviceLostCb,
-                Call(WGPUDeviceLostReason_Unknown, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Unknown,
+                                    SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
 
     constexpr uint8_t data[] = {1, 2, 4, 8};
@@ -172,8 +174,8 @@
         .WillOnce(Return(ByMove(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage))));
 
     // Expect the device lost because of the error.
-    EXPECT_CALL(mDeviceLostCb,
-                Call(WGPUDeviceLostReason_Unknown, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Unknown,
+                                    SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
     device.GetQueue().CopyTextureForBrowser(&src, &dst, &size, &options);
 }
@@ -213,8 +215,8 @@
         .WillOnce(Return(ByMove(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage))));
 
     // Expect the device lost because of the error.
-    EXPECT_CALL(mDeviceLostCb,
-                Call(WGPUDeviceLostReason_Unknown, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Unknown,
+                                    SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
     device.GetQueue().CopyExternalTextureForBrowser(&src, &dst, &size, &options);
 }
@@ -233,8 +235,8 @@
         .WillOnce(Return(ByMove(std::move(computePipelineMock))));
 
     // Expect the device lost because of the error.
-    EXPECT_CALL(mDeviceLostCb,
-                Call(WGPUDeviceLostReason_Unknown, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Unknown,
+                                    SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
     device.CreateComputePipeline(ToCppAPI(&desc));
 }
@@ -259,8 +261,8 @@
         .WillOnce(Return(ByMove(std::move(renderPipelineMock))));
 
     // Expect the device lost because of the error.
-    EXPECT_CALL(mDeviceLostCb,
-                Call(WGPUDeviceLostReason_Unknown, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceLostCb, Call(WGPUDeviceLostReason_Unknown,
+                                    SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
     device.CreateRenderPipeline(ToCppAPI(&desc));
 }
@@ -280,8 +282,8 @@
         .WillOnce(Return(ByMove(std::move(computePipelineMock))));
 
     // Expect the internal error.
-    EXPECT_CALL(mDeviceErrorCb,
-                Call(WGPUErrorType_Internal, HasSubstr(kInternalErrorMessage), this))
+    EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_Internal,
+                                     SizedStringMatches(HasSubstr(kInternalErrorMessage)), this))
         .Times(1);
     device.CreateComputePipeline(ToCppAPI(&desc));
 
@@ -310,8 +312,8 @@
         .WillOnce(Return(ByMove(std::move(renderPipelineMock))));
 
     // Expect the internal error.
-    EXPECT_CALL(mDeviceErrorCb,
-                Call(WGPUErrorType_Internal, HasSubstr(kInternalErrorMessage), this))
+    EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_Internal,
+                                     SizedStringMatches(HasSubstr(kInternalErrorMessage)), this))
         .Times(1);
     device.CreateRenderPipeline(ToCppAPI(&desc));
 
@@ -337,8 +339,8 @@
         .WillOnce(Return(ByMove(std::move(computePipelineMock))));
 
     MockComputePipelineAsyncCallback cb;
-    EXPECT_CALL(
-        cb, Call(wgpu::CreatePipelineAsyncStatus::InternalError, _, HasSubstr(kOomErrorMessage)))
+    EXPECT_CALL(cb, Call(wgpu::CreatePipelineAsyncStatus::InternalError, _,
+                         SizedStringMatches(HasSubstr(kOomErrorMessage))))
         .Times(1);
 
     device.CreateComputePipelineAsync(ToCppAPI(&desc), wgpu::CallbackMode::AllowProcessEvents,
@@ -369,8 +371,8 @@
         .WillOnce(Return(ByMove(std::move(renderPipelineMock))));
 
     MockRenderPipelineAsyncCallback cb;
-    EXPECT_CALL(
-        cb, Call(wgpu::CreatePipelineAsyncStatus::InternalError, _, HasSubstr(kOomErrorMessage)))
+    EXPECT_CALL(cb, Call(wgpu::CreatePipelineAsyncStatus::InternalError, _,
+                         SizedStringMatches(HasSubstr(kOomErrorMessage))))
         .Times(1);
 
     device.CreateRenderPipelineAsync(ToCppAPI(&desc), wgpu::CallbackMode::AllowProcessEvents,
@@ -451,7 +453,8 @@
         .WillOnce(Return(ByMove(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage))));
 
     // Expect the OOM error.
-    EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory,
+                                     SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
 
     wgpu::BufferDescriptor desc = {};
@@ -469,7 +472,8 @@
         .WillOnce(Return(ByMove(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage))));
 
     // Expect the OOM error.
-    EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory,
+                                     SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
 
     wgpu::TextureDescriptor desc = {};
@@ -488,7 +492,8 @@
         .WillOnce(Return(ByMove(DAWN_OUT_OF_MEMORY_ERROR(kOomErrorMessage))));
 
     // Expect the OOM error.
-    EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory,
+                                     SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
 
     wgpu::QuerySetDescriptor desc = {};
@@ -502,7 +507,8 @@
 
 TEST_F(AllowedErrorTests, InjectError) {
     // Expect the OOM error.
-    EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory, HasSubstr(kOomErrorMessage), this))
+    EXPECT_CALL(mDeviceErrorCb, Call(WGPUErrorType_OutOfMemory,
+                                     SizedStringMatches(HasSubstr(kOomErrorMessage)), this))
         .Times(1);
 
     device.InjectError(wgpu::ErrorType::OutOfMemory, kOomErrorMessage);
diff --git a/src/dawn/tests/unittests/native/DeviceCreationTests.cpp b/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
index 5f4e62a..4a1efec 100644
--- a/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
+++ b/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
@@ -36,6 +36,7 @@
 #include "dawn/native/Toggles.h"
 #include "dawn/native/dawn_platform.h"
 #include "dawn/tests/MockCallback.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "dawn/utils/SystemUtils.h"
 #include "dawn/utils/WGPUHelpers.h"
 #include "gtest/gtest.h"
@@ -44,7 +45,9 @@
 namespace {
 
 using testing::Contains;
+using testing::EmptySizedString;
 using testing::MockCallback;
+using testing::NonEmptySizedString;
 using testing::NotNull;
 using testing::SaveArg;
 using testing::StrEq;
@@ -321,7 +324,7 @@
     WGPUDevice cDevice;
     {
         MockCallback<WGPURequestDeviceCallback> cb;
-        EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
+        EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Success, NotNull(), EmptySizedString(), this))
             .WillOnce(SaveArg<1>(&cDevice));
 
         wgpu::DeviceDescriptor desc = {};
@@ -337,7 +340,7 @@
     WGPUDevice cDevice;
     {
         MockCallback<WGPURequestDeviceCallback> cb;
-        EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
+        EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Success, NotNull(), EmptySizedString(), this))
             .WillOnce(SaveArg<1>(&cDevice));
 
         RequestDevice(adapter, nullptr, cb.Callback(), cb.MakeUserdata(this));
@@ -350,7 +353,8 @@
 // Test failing call to RequestDevice with invalid feature
 TEST_P(DeviceCreationFutureTest, RequestDeviceFailure) {
     MockCallback<WGPURequestDeviceCallback> cb;
-    EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Error, nullptr, NotNull(), this)).Times(1);
+    EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Error, nullptr, NonEmptySizedString(), this))
+        .Times(1);
 
     wgpu::DeviceDescriptor desc = {};
     wgpu::FeatureName invalidFeature = static_cast<wgpu::FeatureName>(WGPUFeatureName_Force32);
diff --git a/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp b/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp
index f17c0b5..7f47aea 100644
--- a/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/DeviceValidationTests.cpp
@@ -39,6 +39,7 @@
 using testing::IsNull;
 using testing::MockCppCallback;
 using testing::NotNull;
+using testing::StrEq;
 using testing::WithArgs;
 
 class RequestDeviceValidationTest : public ValidationTest {
@@ -57,7 +58,7 @@
     wgpu::DeviceDescriptor descriptor;
 
     EXPECT_CALL(mRequestDeviceCallback,
-                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), StrEq("")))
         .WillOnce(WithArgs<1>([](wgpu::Device device) {
             // Check one of the default limits.
             wgpu::SupportedLimits limits;
@@ -75,7 +76,7 @@
     descriptor.requiredLimits = &limits;
 
     EXPECT_CALL(mRequestDeviceCallback,
-                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), StrEq("")))
         .WillOnce(WithArgs<1>([](wgpu::Device device) {
             // Check one of the default limits.
             wgpu::SupportedLimits limits;
@@ -99,7 +100,7 @@
     if (supportedLimits.limits.maxBindGroups > 4u) {
         limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups - 1;
         EXPECT_CALL(mRequestDeviceCallback,
-                    Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+                    Call(wgpu::RequestDeviceStatus::Success, NotNull(), StrEq("")))
             .WillOnce(WithArgs<1>([&](wgpu::Device device) {
                 wgpu::SupportedLimits limits;
                 device.GetLimits(&limits);
@@ -116,7 +117,7 @@
     // Test the max.
     limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups;
     EXPECT_CALL(mRequestDeviceCallback,
-                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), StrEq("")))
         .WillOnce(WithArgs<1>([&](wgpu::Device device) {
             wgpu::SupportedLimits limits;
             device.GetLimits(&limits);
@@ -139,7 +140,7 @@
     // Test worse than the default
     limits.limits.maxBindGroups = 3u;
     EXPECT_CALL(mRequestDeviceCallback,
-                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), StrEq("")))
         .WillOnce(WithArgs<1>([&](wgpu::Device device) {
             wgpu::SupportedLimits limits;
             device.GetLimits(&limits);
@@ -172,7 +173,7 @@
     limits.limits.minUniformBufferOffsetAlignment =
         supportedLimits.limits.minUniformBufferOffsetAlignment;
     EXPECT_CALL(mRequestDeviceCallback,
-                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), StrEq("")))
         .WillOnce(WithArgs<1>([&](wgpu::Device device) {
             wgpu::SupportedLimits limits;
             device.GetLimits(&limits);
@@ -191,7 +192,7 @@
         limits.limits.minUniformBufferOffsetAlignment =
             supportedLimits.limits.minUniformBufferOffsetAlignment * 2;
         EXPECT_CALL(mRequestDeviceCallback,
-                    Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+                    Call(wgpu::RequestDeviceStatus::Success, NotNull(), StrEq("")))
             .WillOnce(WithArgs<1>([&](wgpu::Device device) {
                 wgpu::SupportedLimits limits;
                 device.GetLimits(&limits);
@@ -209,7 +210,7 @@
     // Test worse than the default
     limits.limits.minUniformBufferOffsetAlignment = 2u * 256u;
     EXPECT_CALL(mRequestDeviceCallback,
-                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+                Call(wgpu::RequestDeviceStatus::Success, NotNull(), StrEq("")))
         .WillOnce(WithArgs<1>([&](wgpu::Device device) {
             wgpu::SupportedLimits limits;
             device.GetLimits(&limits);
@@ -266,7 +267,7 @@
 
                 if (isSuccess) {
                     EXPECT_CALL(mRequestDeviceCallback,
-                                Call(wgpu::RequestDeviceStatus::Success, NotNull(), IsNull()))
+                                Call(wgpu::RequestDeviceStatus::Success, NotNull(), StrEq("")))
                         .Times(1);
                 } else {
                     EXPECT_CALL(mRequestDeviceCallback,
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.cpp b/src/dawn/tests/unittests/validation/ValidationTest.cpp
index 4c2c075..9446256 100644
--- a/src/dawn/tests/unittests/validation/ValidationTest.cpp
+++ b/src/dawn/tests/unittests/validation/ValidationTest.cpp
@@ -38,9 +38,11 @@
 #include "dawn/native/Adapter.h"
 #include "dawn/native/NullBackend.h"
 #include "dawn/tests/PartitionAllocSupport.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "dawn/tests/ToggleParser.h"
 #include "dawn/tests/unittests/validation/ValidationTest.h"
 #include "dawn/utils/WireHelper.h"
+#include "dawn/webgpu_cpp_print.h"
 
 namespace {
 
@@ -116,7 +118,7 @@
         return dawn::native::GetProcs().instanceRequestAdapter2(
             self, options,
             {nullptr, WGPUCallbackMode_AllowSpontaneous,
-             [](WGPURequestAdapterStatus status, WGPUAdapter cAdapter, char const* message,
+             [](WGPURequestAdapterStatus status, WGPUAdapter cAdapter, WGPUStringView message,
                 void* userdata, void*) {
                  gCurrentTest->mBackendAdapter = dawn::native::FromAPI(cAdapter);
 
@@ -162,7 +164,7 @@
         return dawn::native::GetProcs().adapterRequestDevice2(
             self, reinterpret_cast<WGPUDeviceDescriptor*>(&deviceDesc),
             {nullptr, WGPUCallbackMode_AllowSpontaneous,
-             [](WGPURequestDeviceStatus status, WGPUDevice cDevice, const char* message,
+             [](WGPURequestDeviceStatus status, WGPUDevice cDevice, WGPUStringView message,
                 void* userdata, void*) {
                  gCurrentTest->mLastCreatedBackendDevice = cDevice;
 
@@ -374,7 +376,7 @@
     wgpu::DeviceDescriptor deviceDescriptor = {};
     deviceDescriptor.SetDeviceLostCallback(
         wgpu::CallbackMode::AllowSpontaneous,
-        [this](const wgpu::Device&, wgpu::DeviceLostReason reason, const char* message) {
+        [this](const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView message) {
             if (mExpectDestruction) {
                 EXPECT_EQ(reason, wgpu::DeviceLostReason::Destroyed);
                 return;
@@ -383,7 +385,8 @@
             DAWN_ASSERT(false);
         });
     deviceDescriptor.SetUncapturedErrorCallback(
-        [](const wgpu::Device&, wgpu::ErrorType type, const char* message, ValidationTest* self) {
+        [](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView message,
+           ValidationTest* self) {
             DAWN_ASSERT(type != wgpu::ErrorType::NoError);
 
             ASSERT_TRUE(self->mExpectError) << "Got unexpected device error: " << message;
@@ -394,7 +397,7 @@
 
             self->mDeviceErrorMessage = message;
             if (self->mExpectError) {
-                ASSERT_THAT(message, self->mErrorMatcher);
+                ASSERT_THAT(message, testing::SizedStringMatches(self->mErrorMatcher));
             }
             self->mError = true;
         },
diff --git a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
index 6a182ac..b4b42bc 100644
--- a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
@@ -28,26 +28,28 @@
 #include <unordered_set>
 #include <vector>
 
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/tests/MockCallback.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "dawn/tests/unittests/wire/WireFutureTest.h"
 #include "dawn/tests/unittests/wire/WireTest.h"
-
 #include "dawn/wire/WireClient.h"
 #include "dawn/wire/WireServer.h"
-
 #include "webgpu/webgpu_cpp.h"
 
 namespace dawn::wire {
 namespace {
 
 using testing::_;
+using testing::EmptySizedString;
 using testing::Invoke;
 using testing::InvokeWithoutArgs;
 using testing::MockCallback;
+using testing::NonEmptySizedString;
 using testing::NotNull;
 using testing::Return;
 using testing::SaveArg;
-using testing::StrEq;
+using testing::SizedString;
 using testing::WithArg;
 
 using WireAdapterTestBase = WireFutureTestWithParams<WGPURequestDeviceCallback,
@@ -80,7 +82,7 @@
 
             // Call the callback so the test doesn't wait indefinitely.
             api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
-                                                 nullptr);
+                                                 kEmptyOutputStringView);
         })));
     FlushClient();
     FlushFutures();
@@ -103,7 +105,7 @@
 
             // Call the callback so the test doesn't wait indefinitely.
             api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
-                                                 nullptr);
+                                                 kEmptyOutputStringView);
         })));
     FlushClient();
     FlushFutures();
@@ -116,7 +118,7 @@
 
 static void DeviceLostCallback(const wgpu::Device&,
                                wgpu::DeviceLostReason reason,
-                               const char* message) {}
+                               wgpu::StringView message) {}
 
 // Test that the DeviceDescriptor is not allowed to pass a device lost callback from the client to
 // the server.
@@ -140,7 +142,7 @@
 
             // Call the callback so the test doesn't wait indefinitely.
             api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
-                                                 nullptr);
+                                                 kEmptyOutputStringView);
         })));
     FlushClient();
     FlushFutures();
@@ -196,7 +198,7 @@
             // callback has not been called yet.
             EXPECT_FALSE(GetWireServer()->IsDeviceKnown(apiDevice));
             api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success,
-                                                 apiDevice, nullptr);
+                                                 apiDevice, kEmptyOutputStringView);
             // After the callback is called, the backend device is now known by the server.
             EXPECT_TRUE(GetWireServer()->IsDeviceKnown(apiDevice));
         }));
@@ -207,7 +209,8 @@
     wgpu::Device device;
     // Expect the callback in the client and all the device information to match.
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
+        EXPECT_CALL(mockCb,
+                    Call(WGPURequestDeviceStatus_Success, NotNull(), EmptySizedString(), this))
             .WillOnce(WithArg<1>(Invoke([&](WGPUDevice cDevice) {
                 device = wgpu::Device::Acquire(cDevice);
 
@@ -280,14 +283,16 @@
             // Fake successful creation. The client still receives a failure due to
             // unsupported features.
             api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success,
-                                                 apiDevice, nullptr);
+                                                 apiDevice, kEmptyOutputStringView);
         }));
     FlushClient();
     FlushFutures();
 
     // Expect an error callback since the feature is not supported.
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Error, nullptr, NotNull(), this)).Times(1);
+        EXPECT_CALL(mockCb,
+                    Call(WGPURequestDeviceStatus_Error, nullptr, NonEmptySizedString(), this))
+            .Times(1);
         FlushCallbacks();
     });
 }
@@ -301,7 +306,7 @@
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), _))
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
-                                                 "Request device failed");
+                                                 ToOutputStringView("Request device failed"));
         }));
     FlushClient();
     FlushFutures();
@@ -309,7 +314,7 @@
     // Expect the callback in the client.
     ExpectWireCallbacksWhen([&](auto& mockCb) {
         EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Error, nullptr,
-                                 StrEq("Request device failed"), this))
+                                 SizedString("Request device failed"), this))
             .Times(1);
         FlushCallbacks();
     });
@@ -356,7 +361,7 @@
                 })));
 
             api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success,
-                                                 apiDevice, nullptr);
+                                                 apiDevice, kEmptyOutputStringView);
         }));
     FlushClient();
     FlushFutures();
@@ -364,7 +369,8 @@
     wgpu::Device device;
     // Expect the callback in the client.
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
+        EXPECT_CALL(mockCb,
+                    Call(WGPURequestDeviceStatus_Success, NotNull(), EmptySizedString(), this))
             .WillOnce(WithArg<1>(
                 Invoke([&](WGPUDevice cDevice) { device = wgpu::Device::Acquire(cDevice); })));
         FlushCallbacks();
@@ -384,7 +390,8 @@
     AdapterRequestDevice(adapter, &desc, this);
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_InstanceDropped, nullptr, NotNull(), this))
+        EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_InstanceDropped, nullptr,
+                                 NonEmptySizedString(), this))
             .Times(1);
 
         GetWireClient()->Disconnect();
diff --git a/src/dawn/tests/unittests/wire/WireCreatePipelineAsyncTests.cpp b/src/dawn/tests/unittests/wire/WireCreatePipelineAsyncTests.cpp
index 1666bcc..b5844da 100644
--- a/src/dawn/tests/unittests/wire/WireCreatePipelineAsyncTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireCreatePipelineAsyncTests.cpp
@@ -27,8 +27,10 @@
 
 #include <memory>
 
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/dawn_proc.h"
 #include "dawn/native/DawnNative.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "dawn/tests/unittests/wire/WireFutureTest.h"
 #include "dawn/tests/unittests/wire/WireTest.h"
 #include "dawn/utils/TerribleCommandBuffer.h"
@@ -39,10 +41,12 @@
 namespace {
 
 using testing::_;
+using testing::EmptySizedString;
 using testing::InvokeWithoutArgs;
+using testing::NonEmptySizedString;
 using testing::NotNull;
 using testing::Return;
-using testing::StrEq;
+using testing::SizedString;
 
 using WireCreateComputePipelineAsyncTestBase =
     WireFutureTest<WGPUCreateComputePipelineAsyncCallback,
@@ -131,14 +135,16 @@
 
     EXPECT_CALL(api, OnDeviceCreateComputePipelineAsync2(apiDevice, _, _))
         .WillOnce(InvokeWithoutArgs([&] {
-            api.CallDeviceCreateComputePipelineAsync2Callback(
-                apiDevice, WGPUCreatePipelineAsyncStatus_Success, apiPipeline, "");
+            api.CallDeviceCreateComputePipelineAsync2Callback(apiDevice,
+                                                              WGPUCreatePipelineAsyncStatus_Success,
+                                                              apiPipeline, kEmptyOutputStringView);
         }));
 
     FlushClient();
     FlushFutures();
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(WGPUCreatePipelineAsyncStatus_Success, NotNull(), StrEq(""), this))
+        EXPECT_CALL(mockCb,
+                    Call(WGPUCreatePipelineAsyncStatus_Success, NotNull(), SizedString(""), this))
             .Times(1);
 
         FlushCallbacks();
@@ -153,14 +159,14 @@
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallDeviceCreateComputePipelineAsync2Callback(
                 apiDevice, WGPUCreatePipelineAsyncStatus_ValidationError, nullptr,
-                "Some error message");
+                ToOutputStringView("Some error message"));
         }));
 
     FlushClient();
     FlushFutures();
     ExpectWireCallbacksWhen([&](auto& mockCb) {
         EXPECT_CALL(mockCb, Call(WGPUCreatePipelineAsyncStatus_ValidationError, _,
-                                 StrEq("Some error message"), this))
+                                 SizedString("Some error message"), this))
             .Times(1);
 
         FlushCallbacks();
@@ -173,14 +179,16 @@
 
     EXPECT_CALL(api, OnDeviceCreateRenderPipelineAsync2(apiDevice, _, _))
         .WillOnce(InvokeWithoutArgs([&] {
-            api.CallDeviceCreateRenderPipelineAsync2Callback(
-                apiDevice, WGPUCreatePipelineAsyncStatus_Success, apiPipeline, "");
+            api.CallDeviceCreateRenderPipelineAsync2Callback(apiDevice,
+                                                             WGPUCreatePipelineAsyncStatus_Success,
+                                                             apiPipeline, kEmptyOutputStringView);
         }));
 
     FlushClient();
     FlushFutures();
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(WGPUCreatePipelineAsyncStatus_Success, NotNull(), StrEq(""), this))
+        EXPECT_CALL(mockCb, Call(WGPUCreatePipelineAsyncStatus_Success, NotNull(),
+                                 EmptySizedString(), this))
             .Times(1);
 
         FlushCallbacks();
@@ -195,14 +203,14 @@
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallDeviceCreateRenderPipelineAsync2Callback(
                 apiDevice, WGPUCreatePipelineAsyncStatus_ValidationError, nullptr,
-                "Some error message");
+                ToOutputStringView("Some error message"));
         }));
 
     FlushClient();
     FlushFutures();
     ExpectWireCallbacksWhen([&](auto& mockCb) {
         EXPECT_CALL(mockCb, Call(WGPUCreatePipelineAsyncStatus_ValidationError, _,
-                                 StrEq("Some error message"), this))
+                                 SizedString("Some error message"), this))
             .Times(1);
 
         FlushCallbacks();
@@ -216,14 +224,15 @@
 
     EXPECT_CALL(api, OnDeviceCreateRenderPipelineAsync2(apiDevice, _, _))
         .WillOnce(InvokeWithoutArgs([&] {
-            api.CallDeviceCreateRenderPipelineAsync2Callback(
-                apiDevice, WGPUCreatePipelineAsyncStatus_Success, apiPipeline, "");
+            api.CallDeviceCreateRenderPipelineAsync2Callback(apiDevice,
+                                                             WGPUCreatePipelineAsyncStatus_Success,
+                                                             apiPipeline, kEmptyOutputStringView);
         }));
 
     FlushClient();
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb,
-                    Call(WGPUCreatePipelineAsyncStatus_InstanceDropped, nullptr, NotNull(), this))
+        EXPECT_CALL(mockCb, Call(WGPUCreatePipelineAsyncStatus_InstanceDropped, nullptr,
+                                 NonEmptySizedString(), this))
             .Times(1);
 
         GetWireClient()->Disconnect();
@@ -237,14 +246,15 @@
 
     EXPECT_CALL(api, OnDeviceCreateComputePipelineAsync2(apiDevice, _, _))
         .WillOnce(InvokeWithoutArgs([&] {
-            api.CallDeviceCreateComputePipelineAsync2Callback(
-                apiDevice, WGPUCreatePipelineAsyncStatus_Success, apiPipeline, "");
+            api.CallDeviceCreateComputePipelineAsync2Callback(apiDevice,
+                                                              WGPUCreatePipelineAsyncStatus_Success,
+                                                              apiPipeline, kEmptyOutputStringView);
         }));
 
     FlushClient();
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb,
-                    Call(WGPUCreatePipelineAsyncStatus_InstanceDropped, nullptr, NotNull(), this))
+        EXPECT_CALL(mockCb, Call(WGPUCreatePipelineAsyncStatus_InstanceDropped, nullptr,
+                                 NonEmptySizedString(), this))
             .Times(1);
 
         GetWireClient()->Disconnect();
@@ -257,8 +267,8 @@
     GetWireClient()->Disconnect();
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb,
-                    Call(WGPUCreatePipelineAsyncStatus_InstanceDropped, nullptr, NotNull(), this))
+        EXPECT_CALL(mockCb, Call(WGPUCreatePipelineAsyncStatus_InstanceDropped, nullptr,
+                                 NonEmptySizedString(), this))
             .Times(1);
 
         DeviceCreateRenderPipelineAsync(cDevice, &mDescriptor, this);
@@ -271,8 +281,8 @@
     GetWireClient()->Disconnect();
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb,
-                    Call(WGPUCreatePipelineAsyncStatus_InstanceDropped, nullptr, NotNull(), this))
+        EXPECT_CALL(mockCb, Call(WGPUCreatePipelineAsyncStatus_InstanceDropped, nullptr,
+                                 NonEmptySizedString(), this))
             .Times(1);
 
         DeviceCreateComputePipelineAsync(cDevice, &mDescriptor, this);
@@ -318,7 +328,7 @@
     WGPUAdapter adapter;
     wgpuInstanceRequestAdapter(
         instance, &adapterOptions,
-        [](WGPURequestAdapterStatus status, WGPUAdapter adapter, const char*, void* userdata) {
+        [](WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView, void* userdata) {
             *static_cast<WGPUAdapter*>(userdata) = adapter;
         },
         &adapter);
@@ -329,7 +339,7 @@
     WGPUDevice device;
     wgpuAdapterRequestDevice(
         adapter, &deviceDesc,
-        [](WGPURequestDeviceStatus status, WGPUDevice device, const char*, void* userdata) {
+        [](WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView, void* userdata) {
             *static_cast<WGPUDevice*>(userdata) = device;
         },
         &device);
@@ -350,7 +360,8 @@
     WGPUComputePipeline pipeline = nullptr;
     wgpuDeviceCreateComputePipelineAsync(
         device, &computeDesc,
-        [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline, const char* message,
+        [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline,
+           WGPUStringView message,
            void* userdata) { *static_cast<WGPUComputePipeline*>(userdata) = pipeline; },
         &pipeline);
 
diff --git a/src/dawn/tests/unittests/wire/WireDeviceLifetimeTests.cpp b/src/dawn/tests/unittests/wire/WireDeviceLifetimeTests.cpp
index 6184730..996d0c3 100644
--- a/src/dawn/tests/unittests/wire/WireDeviceLifetimeTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireDeviceLifetimeTests.cpp
@@ -54,7 +54,8 @@
 
         instance.RequestAdapter(
             &options,
-            [](WGPURequestAdapterStatus status, WGPUAdapter cAdapter, const char*, void* userdata) {
+            [](WGPURequestAdapterStatus status, WGPUAdapter cAdapter, WGPUStringView,
+               void* userdata) {
                 ASSERT_EQ(status, WGPURequestAdapterStatus_Success);
                 *static_cast<wgpu::Adapter*>(userdata) = wgpu::Adapter::Acquire(cAdapter);
             },
@@ -81,7 +82,7 @@
             using WrappedUserdata = std::pair<WGPURequestDeviceCallback, void*>;
             native::GetProcs().adapterRequestDevice(
                 self, desc,
-                [](WGPURequestDeviceStatus status, WGPUDevice device, char const* message,
+                [](WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message,
                    void* userdata) {
                     lastBackendDevice = device;
                     auto* wrappedUserdata = static_cast<WrappedUserdata*>(userdata);
@@ -102,7 +103,7 @@
     wgpu::DeviceDescriptor deviceDesc = {};
     adapter.RequestDevice(
         &deviceDesc,
-        [](WGPURequestDeviceStatus, WGPUDevice cDevice, const char*, void* userdata) {
+        [](WGPURequestDeviceStatus, WGPUDevice cDevice, WGPUStringView, void* userdata) {
             *static_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(cDevice);
         },
         &device);
@@ -125,7 +126,7 @@
     // Request a new device. This overrides the wire's device-related data.
     adapter.RequestDevice(
         &deviceDesc,
-        [](WGPURequestDeviceStatus, WGPUDevice cDevice, const char*, void* userdata) {
+        [](WGPURequestDeviceStatus, WGPUDevice cDevice, WGPUStringView, void* userdata) {
             *static_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(cDevice);
         },
         &device);
@@ -145,7 +146,7 @@
     wgpu::DeviceDescriptor deviceDesc = {};
     adapter.RequestDevice(
         &deviceDesc,
-        [](WGPURequestDeviceStatus, WGPUDevice cDevice, const char*, void* userdata) {
+        [](WGPURequestDeviceStatus, WGPUDevice cDevice, WGPUStringView, void* userdata) {
             *static_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(cDevice);
         },
         &device);
@@ -173,7 +174,7 @@
     // Request a new device. This overrides the wire's device-related data.
     adapter.RequestDevice(
         &deviceDesc,
-        [](WGPURequestDeviceStatus, WGPUDevice cDevice, const char*, void* userdata) {
+        [](WGPURequestDeviceStatus, WGPUDevice cDevice, WGPUStringView, void* userdata) {
             *static_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(cDevice);
         },
         &device);
@@ -193,7 +194,7 @@
     wgpu::DeviceDescriptor deviceDesc = {};
     adapter.RequestDevice(
         &deviceDesc,
-        [](WGPURequestDeviceStatus, WGPUDevice cDevice, const char*, void* userdata) {
+        [](WGPURequestDeviceStatus, WGPUDevice cDevice, WGPUStringView, void* userdata) {
             *static_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(cDevice);
         },
         &device);
@@ -213,7 +214,7 @@
     // Request a new device. This overrides the wire's device-related data.
     adapter.RequestDevice(
         &deviceDesc,
-        [](WGPURequestDeviceStatus, WGPUDevice cDevice, const char*, void* userdata) {
+        [](WGPURequestDeviceStatus, WGPUDevice cDevice, WGPUStringView, void* userdata) {
             *static_cast<wgpu::Device*>(userdata) = wgpu::Device::Acquire(cDevice);
         },
         &device);
diff --git a/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp b/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
index 4957945..041fa7f 100644
--- a/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireDisconnectTests.cpp
@@ -28,7 +28,9 @@
 #include "dawn/tests/unittests/wire/WireTest.h"
 
 #include "dawn/common/Assert.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/tests/MockCallback.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "dawn/wire/WireClient.h"
 
 namespace dawn::wire {
@@ -40,7 +42,7 @@
 using testing::MockCallback;
 using testing::Return;
 using testing::Sequence;
-using testing::StrEq;
+using testing::SizedString;
 
 class WireDisconnectTests : public WireTest {};
 
@@ -92,11 +94,11 @@
 // again.
 TEST_F(WireDisconnectTests, ServerLostThenDisconnect) {
     api.CallDeviceSetDeviceLostCallbackCallback(apiDevice, WGPUDeviceLostReason_Unknown,
-                                                "some reason");
+                                                ToOutputStringView("some reason"));
 
     // Flush the device lost return command.
     EXPECT_CALL(deviceLostCallback,
-                Call(_, WGPUDeviceLostReason_Unknown, StrEq("some reason"), this))
+                Call(_, WGPUDeviceLostReason_Unknown, SizedString("some reason"), this))
         .Times(Exactly(1));
     FlushServer();
 
@@ -113,12 +115,12 @@
                                  mockDeviceLostCallback.MakeUserdata(this));
 
     api.CallDeviceSetDeviceLostCallbackCallback(apiDevice, WGPUDeviceLostReason_Unknown,
-                                                "lost reason");
+                                                ToOutputStringView("lost reason"));
 
     // Disconnect the client inside the lost callback. We should see the callback
     // only once.
     EXPECT_CALL(mockDeviceLostCallback,
-                Call(WGPUDeviceLostReason_Unknown, StrEq("lost reason"), this))
+                Call(WGPUDeviceLostReason_Unknown, SizedString("lost reason"), this))
         .WillOnce(InvokeWithoutArgs([&] {
             EXPECT_CALL(mockDeviceLostCallback, Call(_, _, _)).Times(Exactly(0));
             GetWireClient()->Disconnect();
@@ -140,7 +142,7 @@
     // Lose the device on the server. The client callback shouldn't be
     // called again.
     api.CallDeviceSetDeviceLostCallbackCallback(apiDevice, WGPUDeviceLostReason_Unknown,
-                                                "lost reason");
+                                                ToOutputStringView("lost reason"));
     EXPECT_CALL(mockDeviceLostCallback, Call(_, _, _)).Times(Exactly(0));
     FlushServer();
 }
diff --git a/src/dawn/tests/unittests/wire/WireErrorCallbackTests.cpp b/src/dawn/tests/unittests/wire/WireErrorCallbackTests.cpp
index 5e92160..c3e4a11 100644
--- a/src/dawn/tests/unittests/wire/WireErrorCallbackTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireErrorCallbackTests.cpp
@@ -29,6 +29,8 @@
 #include <utility>
 
 #include "dawn/common/FutureUtils.h"
+#include "dawn/common/StringViewUtils.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "dawn/tests/unittests/wire/WireFutureTest.h"
 #include "dawn/tests/unittests/wire/WireTest.h"
 #include "dawn/wire/WireClient.h"
@@ -38,41 +40,42 @@
 
 using testing::_;
 using testing::DoAll;
+using testing::EmptySizedString;
 using testing::InvokeWithoutArgs;
 using testing::Mock;
 using testing::Return;
 using testing::SaveArg;
-using testing::StrEq;
+using testing::SizedString;
 using testing::StrictMock;
 
 // Mock classes to add expectations on the wire calling callbacks
 class MockDeviceErrorCallback {
   public:
-    MOCK_METHOD(void, Call, (WGPUErrorType type, const char* message, void* userdata));
+    MOCK_METHOD(void, Call, (WGPUErrorType type, WGPUStringView message, void* userdata));
 };
 
 std::unique_ptr<StrictMock<MockDeviceErrorCallback>> mockDeviceErrorCallback;
-void ToMockDeviceErrorCallback(WGPUErrorType type, const char* message, void* userdata) {
+void ToMockDeviceErrorCallback(WGPUErrorType type, WGPUStringView message, void* userdata) {
     mockDeviceErrorCallback->Call(type, message, userdata);
 }
 
 class MockDeviceLoggingCallback {
   public:
-    MOCK_METHOD(void, Call, (WGPULoggingType type, const char* message, void* userdata));
+    MOCK_METHOD(void, Call, (WGPULoggingType type, WGPUStringView message, void* userdata));
 };
 
 std::unique_ptr<StrictMock<MockDeviceLoggingCallback>> mockDeviceLoggingCallback;
-void ToMockDeviceLoggingCallback(WGPULoggingType type, const char* message, void* userdata) {
+void ToMockDeviceLoggingCallback(WGPULoggingType type, WGPUStringView message, void* userdata) {
     mockDeviceLoggingCallback->Call(type, message, userdata);
 }
 
 class MockDeviceLostCallback {
   public:
-    MOCK_METHOD(void, Call, (WGPUDeviceLostReason reason, const char* message, void* userdata));
+    MOCK_METHOD(void, Call, (WGPUDeviceLostReason reason, WGPUStringView message, void* userdata));
 };
 
 std::unique_ptr<StrictMock<MockDeviceLostCallback>> mockDeviceLostCallback;
-void ToMockDeviceLostCallback(WGPUDeviceLostReason reason, const char* message, void* userdata) {
+void ToMockDeviceLostCallback(WGPUDeviceLostReason reason, WGPUStringView message, void* userdata) {
     mockDeviceLostCallback->Call(reason, message, userdata);
 }
 
@@ -114,10 +117,10 @@
     // Calling the callback on the server side will result in the callback being called on the
     // client side
     api.CallDeviceSetUncapturedErrorCallbackCallback(apiDevice, WGPUErrorType_Validation,
-                                                     "Some error message");
+                                                     ToOutputStringView("Some error message"));
 
     EXPECT_CALL(*mockDeviceErrorCallback,
-                Call(WGPUErrorType_Validation, StrEq("Some error message"), this))
+                Call(WGPUErrorType_Validation, SizedString("Some error message"), this))
         .Times(1);
 
     FlushServer();
@@ -133,10 +136,10 @@
     // Calling the callback on the server side will result in the callback being called on the
     // client side
     api.CallDeviceSetUncapturedErrorCallbackCallback(apiDevice, WGPUErrorType_OutOfMemory,
-                                                     "Some error message");
+                                                     ToOutputStringView("Some error message"));
 
     EXPECT_CALL(*mockDeviceErrorCallback,
-                Call(WGPUErrorType_OutOfMemory, StrEq("Some error message"), this))
+                Call(WGPUErrorType_OutOfMemory, SizedString("Some error message"), this))
         .Times(1);
 
     FlushServer();
@@ -152,10 +155,10 @@
     // Calling the callback on the server side will result in the callback being called on the
     // client side
     api.CallDeviceSetUncapturedErrorCallbackCallback(apiDevice, WGPUErrorType_Internal,
-                                                     "Some error message");
+                                                     ToOutputStringView("Some error message"));
 
     EXPECT_CALL(*mockDeviceErrorCallback,
-                Call(WGPUErrorType_Internal, StrEq("Some error message"), this))
+                Call(WGPUErrorType_Internal, SizedString("Some error message"), this))
         .Times(1);
 
     FlushServer();
@@ -170,9 +173,11 @@
 
     // Calling the callback on the server side will result in the callback being called on the
     // client side
-    api.CallDeviceSetLoggingCallbackCallback(apiDevice, WGPULoggingType_Info, "Some message");
+    api.CallDeviceSetLoggingCallbackCallback(apiDevice, WGPULoggingType_Info,
+                                             ToOutputStringView("Some message"));
 
-    EXPECT_CALL(*mockDeviceLoggingCallback, Call(WGPULoggingType_Info, StrEq("Some message"), this))
+    EXPECT_CALL(*mockDeviceLoggingCallback,
+                Call(WGPULoggingType_Info, SizedString("Some message"), this))
         .Times(1);
 
     FlushServer();
@@ -188,10 +193,10 @@
     // Calling the callback on the server side will result in the callback being called on the
     // client side
     api.CallDeviceSetDeviceLostCallbackCallback(apiDevice, WGPUDeviceLostReason_Unknown,
-                                                "Some error message");
+                                                ToOutputStringView("Some error message"));
 
     EXPECT_CALL(*mockDeviceLostCallback,
-                Call(WGPUDeviceLostReason_Unknown, StrEq("Some error message"), this))
+                Call(WGPUDeviceLostReason_Unknown, SizedString("Some error message"), this))
         .Times(1);
 
     FlushServer();
@@ -252,20 +257,21 @@
         DevicePopErrorScope(device, this);
         EXPECT_CALL(api, OnDevicePopErrorScope2(apiDevice, _)).WillOnce([&] {
             api.CallDevicePopErrorScope2Callback(apiDevice, WGPUPopErrorScopeStatus_Success, type,
-                                                 "Some error message");
+                                                 ToOutputStringView("Some error message"));
         });
 
         FlushClient();
         FlushFutures();
         ExpectWireCallbacksWhen(
             [&](auto& oldMockCb) {
-                EXPECT_CALL(oldMockCb, Call(type, StrEq("Some error message"), this)).Times(1);
+                EXPECT_CALL(oldMockCb, Call(type, SizedString("Some error message"), this))
+                    .Times(1);
 
                 FlushCallbacks();
             },
             [&](auto& mockCb) {
                 EXPECT_CALL(mockCb, Call(WGPUPopErrorScopeStatus_Success, type,
-                                         StrEq("Some error message"), this))
+                                         SizedString("Some error message"), this))
                     .Times(1);
 
                 FlushCallbacks();
@@ -285,13 +291,13 @@
     FlushFutures();
     ExpectWireCallbacksWhen(
         [&](auto& oldMockCb) {
-            EXPECT_CALL(oldMockCb, Call(WGPUErrorType_Unknown, nullptr, this)).Times(1);
+            EXPECT_CALL(oldMockCb, Call(WGPUErrorType_Unknown, EmptySizedString(), this)).Times(1);
 
             GetWireClient()->Disconnect();
         },
         [&](auto& mockCb) {
             EXPECT_CALL(mockCb, Call(WGPUPopErrorScopeStatus_InstanceDropped, WGPUErrorType_Unknown,
-                                     nullptr, this))
+                                     EmptySizedString(), this))
                 .Times(1);
 
             GetWireClient()->Disconnect();
@@ -309,20 +315,22 @@
     DevicePopErrorScope(device, this);
     EXPECT_CALL(api, OnDevicePopErrorScope2(apiDevice, _)).WillOnce(InvokeWithoutArgs([&] {
         api.CallDevicePopErrorScope2Callback(apiDevice, WGPUPopErrorScopeStatus_Success,
-                                             WGPUErrorType_Validation, "Some error message");
+                                             WGPUErrorType_Validation,
+                                             ToOutputStringView("Some error message"));
     }));
 
     FlushClient();
     FlushFutures();
     ExpectWireCallbacksWhen(
         [&](auto& oldMockCb) {
-            EXPECT_CALL(oldMockCb, Call(WGPUErrorType_Validation, nullptr, this)).Times(1);
+            EXPECT_CALL(oldMockCb, Call(WGPUErrorType_Validation, EmptySizedString(), this))
+                .Times(1);
 
             GetWireClient()->Disconnect();
         },
         [&](auto& mockCb) {
             EXPECT_CALL(mockCb, Call(WGPUPopErrorScopeStatus_InstanceDropped,
-                                     WGPUErrorType_Validation, nullptr, this))
+                                     WGPUErrorType_Validation, EmptySizedString(), this))
                 .Times(1);
 
             GetWireClient()->Disconnect();
@@ -334,7 +342,8 @@
     DevicePopErrorScope(cDevice, this);
     EXPECT_CALL(api, OnDevicePopErrorScope2(apiDevice, _)).WillOnce(InvokeWithoutArgs([&] {
         api.CallDevicePopErrorScope2Callback(apiDevice, WGPUPopErrorScopeStatus_Success,
-                                             WGPUErrorType_Validation, "No error scopes to pop");
+                                             WGPUErrorType_Validation,
+                                             ToOutputStringView("No error scopes to pop"));
     }));
 
     FlushClient();
@@ -342,14 +351,14 @@
     ExpectWireCallbacksWhen(
         [&](auto& oldMockCb) {
             EXPECT_CALL(oldMockCb,
-                        Call(WGPUErrorType_Validation, StrEq("No error scopes to pop"), this))
+                        Call(WGPUErrorType_Validation, SizedString("No error scopes to pop"), this))
                 .Times(1);
 
             FlushCallbacks();
         },
         [&](auto& mockCb) {
             EXPECT_CALL(mockCb, Call(WGPUPopErrorScopeStatus_Success, WGPUErrorType_Validation,
-                                     StrEq("No error scopes to pop"), this))
+                                     SizedString("No error scopes to pop"), this))
                 .Times(1);
 
             FlushCallbacks();
diff --git a/src/dawn/tests/unittests/wire/WireInjectInstanceTests.cpp b/src/dawn/tests/unittests/wire/WireInjectInstanceTests.cpp
index bf123d1..100144b 100644
--- a/src/dawn/tests/unittests/wire/WireInjectInstanceTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireInjectInstanceTests.cpp
@@ -27,6 +27,7 @@
 
 #include <array>
 
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/tests/MockCallback.h"
 #include "dawn/tests/unittests/wire/WireTest.h"
 #include "dawn/wire/WireClient.h"
@@ -62,7 +63,7 @@
 
     EXPECT_CALL(api, OnInstanceRequestAdapter2(apiInstance, _, _)).WillOnce([&]() {
         api.CallInstanceRequestAdapter2Callback(apiInstance, WGPURequestAdapterStatus_Error,
-                                                nullptr, "Some error message.");
+                                                nullptr, ToOutputStringView("Some error message."));
     });
     FlushClient();
 
diff --git a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
index f6865a7..a2950f7 100644
--- a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
@@ -30,6 +30,7 @@
 
 #include "dawn/common/StringViewUtils.h"
 #include "dawn/tests/MockCallback.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "dawn/tests/unittests/wire/WireFutureTest.h"
 #include "dawn/tests/unittests/wire/WireTest.h"
 
@@ -42,13 +43,16 @@
 namespace {
 
 using testing::_;
+using testing::EmptySizedString;
 using testing::Invoke;
 using testing::InvokeWithoutArgs;
 using testing::MockCallback;
 using testing::NiceMock;
+using testing::NonEmptySizedString;
 using testing::NotNull;
 using testing::Return;
 using testing::SetArgPointee;
+using testing::SizedString;
 using testing::StrEq;
 using testing::WithArg;
 
@@ -101,7 +105,7 @@
                           static_cast<WGPUPowerPreference>(options.powerPreference));
                 EXPECT_EQ(apiOptions->forceFallbackAdapter, options.forceFallbackAdapter);
                 api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Error,
-                                                       nullptr, nullptr);
+                                                       nullptr, kEmptyOutputStringView);
             })));
 
         FlushClient();
@@ -168,7 +172,7 @@
                     return fakeFeatures.size();
                 })));
             api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success,
-                                                   apiAdapter, nullptr);
+                                                   apiAdapter, kEmptyOutputStringView);
         }));
 
     FlushClient();
@@ -176,7 +180,8 @@
 
     // Expect the callback in the client and all the adapter information to match.
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, nullptr))
+        EXPECT_CALL(mockCb,
+                    Call(WGPURequestAdapterStatus_Success, NotNull(), EmptySizedString(), nullptr))
             .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter adapter) {
                 WGPUAdapterInfo info = {};
                 wgpuAdapterGetInfo(adapter, &info);
@@ -305,7 +310,7 @@
                     return fakeFeatures.size();
                 })));
             api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success,
-                                                   apiAdapter, nullptr);
+                                                   apiAdapter, kEmptyOutputStringView);
         }));
 
     FlushClient();
@@ -313,7 +318,8 @@
 
     // Expect the callback in the client and the adapter information to match.
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, nullptr))
+        EXPECT_CALL(mockCb,
+                    Call(WGPURequestAdapterStatus_Success, NotNull(), EmptySizedString(), nullptr))
             .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter adapter) {
                 // Request info without a chained struct.
                 // It should be nullptr.
@@ -401,7 +407,7 @@
                     return fakeFeatures.size();
                 })));
             api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success,
-                                                   apiAdapter, nullptr);
+                                                   apiAdapter, kEmptyOutputStringView);
         }));
 
     FlushClient();
@@ -409,7 +415,8 @@
 
     // Expect the callback in the client and all the adapter information to match.
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, nullptr))
+        EXPECT_CALL(mockCb,
+                    Call(WGPURequestAdapterStatus_Success, NotNull(), EmptySizedString(), nullptr))
             .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter adapter) {
                 WGPUFeatureName feature;
                 ASSERT_EQ(wgpuAdapterEnumerateFeatures(adapter, nullptr), 1u);
@@ -430,7 +437,7 @@
     EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), _))
         .WillOnce(InvokeWithoutArgs([&] {
             api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Error,
-                                                   nullptr, "Some error");
+                                                   nullptr, ToOutputStringView("Some error"));
         }));
 
     FlushClient();
@@ -438,8 +445,8 @@
 
     // Expect the callback in the client.
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb,
-                    Call(WGPURequestAdapterStatus_Error, nullptr, StrEq("Some error"), nullptr))
+        EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_Error, nullptr, SizedString("Some error"),
+                                 nullptr))
             .Times(1);
 
         FlushCallbacks();
@@ -456,7 +463,8 @@
     InstanceRequestAdapter(instance, &options, nullptr);
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_Unknown, nullptr, NotNull(), nullptr))
+        EXPECT_CALL(mockCb,
+                    Call(WGPURequestAdapterStatus_Unknown, nullptr, NonEmptySizedString(), nullptr))
             .Times(1);
 
         instance = nullptr;
@@ -470,8 +478,8 @@
     InstanceRequestAdapter(instance, &options, nullptr);
 
     ExpectWireCallbacksWhen([&](auto& mockCb) {
-        EXPECT_CALL(mockCb,
-                    Call(WGPURequestAdapterStatus_InstanceDropped, nullptr, NotNull(), nullptr))
+        EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_InstanceDropped, nullptr,
+                                 NonEmptySizedString(), nullptr))
             .Times(1);
 
         GetWireClient()->Disconnect();
diff --git a/src/dawn/tests/unittests/wire/WireTest.cpp b/src/dawn/tests/unittests/wire/WireTest.cpp
index fcfcaca..f254a14 100644
--- a/src/dawn/tests/unittests/wire/WireTest.cpp
+++ b/src/dawn/tests/unittests/wire/WireTest.cpp
@@ -44,17 +44,20 @@
 using testing::NotNull;
 using testing::Return;
 using testing::SaveArg;
+using testing::StrEq;
 using testing::WithArg;
 
+namespace dawn {
+
 WireTest::WireTest() {}
 
 WireTest::~WireTest() {}
 
-dawn::wire::client::MemoryTransferService* WireTest::GetClientMemoryTransferService() {
+wire::client::MemoryTransferService* WireTest::GetClientMemoryTransferService() {
     return nullptr;
 }
 
-dawn::wire::server::MemoryTransferService* WireTest::GetServerMemoryTransferService() {
+wire::server::MemoryTransferService* WireTest::GetServerMemoryTransferService() {
     return nullptr;
 }
 
@@ -63,25 +66,25 @@
     api.GetProcTable(&mockProcs);
     SetupIgnoredCallExpectations();
 
-    mS2cBuf = std::make_unique<dawn::utils::TerribleCommandBuffer>();
-    mC2sBuf = std::make_unique<dawn::utils::TerribleCommandBuffer>(mWireServer.get());
+    mS2cBuf = std::make_unique<utils::TerribleCommandBuffer>();
+    mC2sBuf = std::make_unique<utils::TerribleCommandBuffer>(mWireServer.get());
 
-    dawn::wire::WireServerDescriptor serverDesc = {};
+    wire::WireServerDescriptor serverDesc = {};
     serverDesc.procs = &mockProcs;
     serverDesc.serializer = mS2cBuf.get();
     serverDesc.memoryTransferService = GetServerMemoryTransferService();
 
-    mWireServer.reset(new dawn::wire::WireServer(serverDesc));
+    mWireServer.reset(new wire::WireServer(serverDesc));
     mC2sBuf->SetHandler(mWireServer.get());
 
-    dawn::wire::WireClientDescriptor clientDesc = {};
+    wire::WireClientDescriptor clientDesc = {};
     clientDesc.serializer = mC2sBuf.get();
     clientDesc.memoryTransferService = GetClientMemoryTransferService();
 
-    mWireClient.reset(new dawn::wire::WireClient(clientDesc));
+    mWireClient.reset(new wire::WireClient(clientDesc));
     mS2cBuf->SetHandler(mWireClient.get());
 
-    dawnProcSetProcs(&dawn::wire::client::GetProcs());
+    dawnProcSetProcs(&wire::client::GetProcs());
 
     auto reservedInstance = GetWireClient()->ReserveInstance();
     instance = wgpu::Instance::Acquire(reservedInstance.instance);
@@ -101,10 +104,10 @@
         EXPECT_CALL(api, AdapterGetInfo(apiAdapter, NotNull()))
             .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterInfo* info) {
                 *info = {};
-                info->vendor = dawn::kEmptyOutputStringView;
-                info->architecture = dawn::kEmptyOutputStringView;
-                info->device = dawn::kEmptyOutputStringView;
-                info->description = dawn::kEmptyOutputStringView;
+                info->vendor = kEmptyOutputStringView;
+                info->architecture = kEmptyOutputStringView;
+                info->device = kEmptyOutputStringView;
+                info->description = kEmptyOutputStringView;
                 return WGPUStatus_Success;
             })));
 
@@ -119,10 +122,10 @@
             .WillOnce(Return(0));
 
         api.CallInstanceRequestAdapter2Callback(apiInstance, WGPURequestAdapterStatus_Success,
-                                                apiAdapter, nullptr);
+                                                apiAdapter, kEmptyOutputStringView);
     });
     FlushClient();
-    EXPECT_CALL(adapterCb, Call(wgpu::RequestAdapterStatus::Success, NotNull(), nullptr, this))
+    EXPECT_CALL(adapterCb, Call(wgpu::RequestAdapterStatus::Success, NotNull(), StrEq(""), this))
         .WillOnce(SaveArg<1>(&adapter));
     FlushServer();
     EXPECT_NE(adapter, nullptr);
@@ -167,10 +170,10 @@
                 .WillOnce(Return(0));
 
             api.CallAdapterRequestDevice2Callback(apiAdapter, WGPURequestDeviceStatus_Success,
-                                                  apiDevice, nullptr);
+                                                  apiDevice, kEmptyOutputStringView);
         }));
     FlushClient();
-    EXPECT_CALL(deviceCb, Call(wgpu::RequestDeviceStatus::Success, NotNull(), nullptr, this))
+    EXPECT_CALL(deviceCb, Call(wgpu::RequestDeviceStatus::Success, NotNull(), StrEq(""), this))
         .WillOnce(SaveArg<1>(&device));
     FlushServer();
     EXPECT_NE(device, nullptr);
@@ -232,11 +235,11 @@
     ASSERT_EQ(mS2cBuf->Flush(), success);
 }
 
-dawn::wire::WireServer* WireTest::GetWireServer() {
+wire::WireServer* WireTest::GetWireServer() {
     return mWireServer.get();
 }
 
-dawn::wire::WireClient* WireTest::GetWireClient() {
+wire::WireClient* WireTest::GetWireClient() {
     return mWireClient.get();
 }
 
@@ -264,3 +267,5 @@
     EXPECT_CALL(api, InstanceProcessEvents(_)).Times(AnyNumber());
     EXPECT_CALL(api, DeviceTick(_)).Times(AnyNumber());
 }
+
+}  // namespace dawn
diff --git a/src/dawn/tests/unittests/wire/WireTest.h b/src/dawn/tests/unittests/wire/WireTest.h
index a64950f..0acb7fb 100644
--- a/src/dawn/tests/unittests/wire/WireTest.h
+++ b/src/dawn/tests/unittests/wire/WireTest.h
@@ -37,6 +37,8 @@
 
 #include "webgpu/webgpu_cpp.h"
 
+namespace dawn {
+
 // Definition of a "Lambda predicate matcher" for GMock to allow checking deep structures
 // are passed correctly by the wire.
 
@@ -119,7 +121,7 @@
         }                                                       \
     } while (0)
 
-namespace dawn::wire {
+namespace wire {
 class WireClient;
 class WireServer;
 namespace client {
@@ -128,11 +130,11 @@
 namespace server {
 class MemoryTransferService;
 }  // namespace server
-}  // namespace dawn::wire
+}  // namespace wire
 
-namespace dawn::utils {
+namespace utils {
 class TerribleCommandBuffer;
-}
+}  // namespace utils
 
 class WireTest : public testing::Test {
   protected:
@@ -183,4 +185,6 @@
     std::unique_ptr<dawn::utils::TerribleCommandBuffer> mC2sBuf;
 };
 
+}  // namespace dawn
+
 #endif  // SRC_DAWN_TESTS_UNITTESTS_WIRE_WIRETEST_H_
diff --git a/src/dawn/wire/client/Adapter.cpp b/src/dawn/wire/client/Adapter.cpp
index 3c07a4b..8ed8052 100644
--- a/src/dawn/wire/client/Adapter.cpp
+++ b/src/dawn/wire/client/Adapter.cpp
@@ -62,15 +62,13 @@
 
     WireResult ReadyHook(FutureID futureID,
                          WGPURequestDeviceStatus status,
-                         const char* message,
+                         WGPUStringView message,
                          const WGPUSupportedLimits* limits,
                          uint32_t featuresCount,
                          const WGPUFeatureName* features) {
         DAWN_ASSERT(mDevice != nullptr);
         mStatus = status;
-        if (message != nullptr) {
-            mMessage = message;
-        }
+        mMessage = ToString(message);
         if (status == WGPURequestDeviceStatus_Success) {
             mDevice->SetLimits(limits);
             mDevice->SetFeatures(features, featuresCount);
@@ -91,13 +89,13 @@
             mCallback(mStatus,
                       mStatus == WGPURequestDeviceStatus_Success ? ReturnToAPI(std::move(device))
                                                                  : nullptr,
-                      mMessage ? mMessage->c_str() : nullptr, mUserdata1.ExtractAsDangling());
+                      ToOutputStringView(mMessage), mUserdata1.ExtractAsDangling());
         } else if (mCallback2) {
             Ref<Device> device = mDevice;
             mCallback2(mStatus,
                        mStatus == WGPURequestDeviceStatus_Success ? ReturnToAPI(std::move(device))
                                                                   : nullptr,
-                       mMessage ? mMessage->c_str() : nullptr, mUserdata1.ExtractAsDangling(),
+                       ToOutputStringView(mMessage), mUserdata1.ExtractAsDangling(),
                        mUserdata2.ExtractAsDangling());
         }
 
@@ -105,11 +103,12 @@
             // If there was an error and we didn't return a device, we need to call the device lost
             // callback and reclaim the device allocation.
             if (mStatus == WGPURequestDeviceStatus_InstanceDropped) {
-                mDevice->HandleDeviceLost(WGPUDeviceLostReason_InstanceDropped,
-                                          "A valid external Instance reference no longer exists.");
+                mDevice->HandleDeviceLost(
+                    WGPUDeviceLostReason_InstanceDropped,
+                    ToOutputStringView("A valid external Instance reference no longer exists."));
             } else {
                 mDevice->HandleDeviceLost(WGPUDeviceLostReason_FailedCreation,
-                                          "Device failed at creation.");
+                                          ToOutputStringView("Device failed at creation."));
             }
         }
 
@@ -128,7 +127,7 @@
     // Note that the message is optional because we want to return nullptr when it wasn't set
     // instead of a pointer to an empty string.
     WGPURequestDeviceStatus mStatus;
-    std::optional<std::string> mMessage;
+    std::string mMessage;
 
     // The device is created when we call RequestDevice(F). It is guaranteed to be alive
     // throughout the duration of a RequestDeviceEvent because the Event essentially takes
@@ -350,7 +349,7 @@
 WireResult Client::DoAdapterRequestDeviceCallback(ObjectHandle eventManager,
                                                   WGPUFuture future,
                                                   WGPURequestDeviceStatus status,
-                                                  const char* message,
+                                                  WGPUStringView message,
                                                   const WGPUSupportedLimits* limits,
                                                   uint32_t featuresCount,
                                                   const WGPUFeatureName* features) {
diff --git a/src/dawn/wire/client/Buffer.cpp b/src/dawn/wire/client/Buffer.cpp
index e9a50c9..9f2c78b 100644
--- a/src/dawn/wire/client/Buffer.cpp
+++ b/src/dawn/wire/client/Buffer.cpp
@@ -32,6 +32,7 @@
 #include <string>
 #include <utility>
 
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/wire/BufferConsumer_impl.h"
 #include "dawn/wire/WireCmd_autogen.h"
 #include "dawn/wire/client/Client.h"
@@ -226,12 +227,12 @@
 
     WireResult ReadyHook(FutureID futureID,
                          WGPUMapAsyncStatus status,
-                         const char* message,
+                         WGPUStringView message,
                          uint64_t readDataUpdateInfoLength = 0,
                          const uint8_t* readDataUpdateInfo = nullptr) {
         if (status != WGPUMapAsyncStatus_Success) {
             mStatus = status;
-            mMessage = message;
+            mMessage = ToString(message);
             return WireResult::Success;
         }
 
@@ -248,7 +249,6 @@
         };
 
         mStatus = status;
-        DAWN_ASSERT(message == nullptr);
         const auto& pending = mBuffer->mPendingMapRequest.value();
         if (!pending.type) {
             return FailRequest("Invalid map call without a specified mapping type.");
@@ -290,8 +290,8 @@
 
         auto Callback = [this]() {
             if (mCallback) {
-                mCallback(mStatus, mMessage ? mMessage->c_str() : nullptr,
-                          mUserdata1.ExtractAsDangling(), mUserdata2.ExtractAsDangling());
+                mCallback(mStatus, ToOutputStringView(mMessage), mUserdata1.ExtractAsDangling(),
+                          mUserdata2.ExtractAsDangling());
             }
         };
 
@@ -327,7 +327,7 @@
     raw_ptr<void> mUserdata2;
 
     WGPUMapAsyncStatus mStatus;
-    std::optional<std::string> mMessage;
+    std::string mMessage;
 
     // Strong reference to the buffer so that when we call the callback we can pass the buffer.
     Ref<Buffer> mBuffer;
@@ -476,7 +476,7 @@
 
     if (isNewEntryPoint) {
         auto [newStatus, message] =
-            [](WGPUBufferMapAsyncStatus status) -> std::pair<WGPUMapAsyncStatus, const char*> {
+            [](WGPUBufferMapAsyncStatus status) -> std::pair<WGPUMapAsyncStatus, std::string_view> {
             switch (status) {
                 case WGPUBufferMapAsyncStatus_DestroyedBeforeCallback:
                     return {WGPUMapAsyncStatus_Aborted,
@@ -489,8 +489,8 @@
             }
         }(status);
 
-        DAWN_CHECK(GetEventManager().SetFutureReady<MapAsyncEvent2>(futureID, newStatus, message) ==
-                   WireResult::Success);
+        DAWN_CHECK(GetEventManager().SetFutureReady<MapAsyncEvent2>(
+                       futureID, newStatus, ToOutputStringView(message)) == WireResult::Success);
     } else {
         DAWN_CHECK(GetEventManager().SetFutureReady<MapAsyncEvent>(futureID, status) ==
                    WireResult::Success);
@@ -569,7 +569,7 @@
     if (mPendingMapRequest) {
         [[maybe_unused]] auto id = GetEventManager().SetFutureReady<MapAsyncEvent2>(
             futureIDInternal, WGPUMapAsyncStatus_Error,
-            "Buffer already has an outstanding map pending.");
+            ToOutputStringView("Buffer already has an outstanding map pending."));
         return {futureIDInternal};
     }
 
@@ -606,7 +606,7 @@
                                             WGPUFuture future,
                                             WGPUBufferMapAsyncStatus status,
                                             WGPUMapAsyncStatus status2,
-                                            const char* message,
+                                            WGPUStringView message,
                                             uint8_t userdataCount,
                                             uint64_t readDataUpdateInfoLength,
                                             const uint8_t* readDataUpdateInfo) {
diff --git a/src/dawn/wire/client/Client.cpp b/src/dawn/wire/client/Client.cpp
index 56f20b9..2996660 100644
--- a/src/dawn/wire/client/Client.cpp
+++ b/src/dawn/wire/client/Client.cpp
@@ -28,6 +28,7 @@
 #include "dawn/wire/client/Client.h"
 
 #include "dawn/common/Compiler.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/wire/client/Device.h"
 
 namespace dawn::wire::client {
@@ -161,8 +162,8 @@
         auto& deviceList = mObjects[ObjectType::Device];
         for (auto object : deviceList.GetAllObjects()) {
             if (object != nullptr) {
-                static_cast<Device*>(object)->HandleDeviceLost(WGPUDeviceLostReason_Unknown,
-                                                               "GPU connection lost");
+                static_cast<Device*>(object)->HandleDeviceLost(
+                    WGPUDeviceLostReason_Unknown, ToOutputStringView("GPU connection lost"));
             }
         }
     }
diff --git a/src/dawn/wire/client/ClientDoers.cpp b/src/dawn/wire/client/ClientDoers.cpp
index 0d74781..4931291 100644
--- a/src/dawn/wire/client/ClientDoers.cpp
+++ b/src/dawn/wire/client/ClientDoers.cpp
@@ -35,7 +35,7 @@
 
 WireResult Client::DoDeviceUncapturedErrorCallback(Device* device,
                                                    WGPUErrorType errorType,
-                                                   const char* message) {
+                                                   WGPUStringView message) {
     switch (errorType) {
         case WGPUErrorType_NoError:
         case WGPUErrorType_Validation:
@@ -57,7 +57,7 @@
 
 WireResult Client::DoDeviceLoggingCallback(Device* device,
                                            WGPULoggingType loggingType,
-                                           const char* message) {
+                                           WGPUStringView message) {
     if (device == nullptr) {
         // The device might have been deleted or recreated so this isn't an error.
         return WireResult::Success;
diff --git a/src/dawn/wire/client/Device.cpp b/src/dawn/wire/client/Device.cpp
index 45ef133..851f16c 100644
--- a/src/dawn/wire/client/Device.cpp
+++ b/src/dawn/wire/client/Device.cpp
@@ -33,6 +33,7 @@
 
 #include "dawn/common/Assert.h"
 #include "dawn/common/Log.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/wire/client/ApiObjects_autogen.h"
 #include "dawn/wire/client/Client.h"
 #include "dawn/wire/client/EventManager.h"
@@ -53,11 +54,9 @@
 
     EventType GetType() override { return kType; }
 
-    WireResult ReadyHook(FutureID futureID, WGPUErrorType errorType, const char* message) {
+    WireResult ReadyHook(FutureID futureID, WGPUErrorType errorType, WGPUStringView message) {
         mType = errorType;
-        if (message != nullptr) {
-            mMessage = message;
-        }
+        mMessage = ToString(message);
         return WireResult::Success;
     }
 
@@ -65,11 +64,11 @@
     void CompleteImpl(FutureID futureID, EventCompletionType completionType) override {
         if (completionType == EventCompletionType::Shutdown) {
             mStatus = WGPUPopErrorScopeStatus_InstanceDropped;
-            mMessage = std::nullopt;
+            mMessage = "";
         }
         if (mCallback) {
-            mCallback(mStatus, mType, mMessage ? mMessage->c_str() : nullptr,
-                      mUserdata1.ExtractAsDangling(), mUserdata2.ExtractAsDangling());
+            mCallback(mStatus, mType, ToOutputStringView(mMessage), mUserdata1.ExtractAsDangling(),
+                      mUserdata2.ExtractAsDangling());
         }
     }
 
@@ -79,7 +78,7 @@
 
     WGPUPopErrorScopeStatus mStatus = WGPUPopErrorScopeStatus_Success;
     WGPUErrorType mType = WGPUErrorType_Unknown;
-    std::optional<std::string> mMessage;
+    std::string mMessage;
 };
 
 template <typename PipelineT, EventType Type, typename CallbackInfoT>
@@ -104,12 +103,10 @@
 
     WireResult ReadyHook(FutureID futureID,
                          WGPUCreatePipelineAsyncStatus status,
-                         const char* message) {
+                         WGPUStringView message) {
         DAWN_ASSERT(mPipeline != nullptr);
         mStatus = status;
-        if (message != nullptr) {
-            mMessage = message;
-        }
+        mMessage = ToString(message);
         return WireResult::Success;
     }
 
@@ -131,7 +128,7 @@
                   mStatus == WGPUCreatePipelineAsyncStatus_Success
                       ? ReturnToAPI(std::move(mPipeline))
                       : nullptr,
-                  mMessage ? mMessage->c_str() : nullptr, userdata1, userdata2);
+                  ToOutputStringView(mMessage), userdata1, userdata2);
     }
 
     using Callback = decltype(std::declval<CallbackInfo>().callback);
@@ -142,7 +139,7 @@
     // Note that the message is optional because we want to return nullptr when it wasn't set
     // instead of a pointer to an empty string.
     WGPUCreatePipelineAsyncStatus mStatus = WGPUCreatePipelineAsyncStatus_Success;
-    std::optional<std::string> mMessage;
+    std::string mMessage;
 
     Ref<Pipeline> mPipeline;
 };
@@ -158,7 +155,7 @@
 
 void LegacyDeviceLostCallback(WGPUDevice const*,
                               WGPUDeviceLostReason reason,
-                              char const* message,
+                              WGPUStringView message,
                               void* callback,
                               void* userdata) {
     if (callback == nullptr) {
@@ -170,7 +167,7 @@
 
 void LegacyDeviceLostCallback2(WGPUDevice const* device,
                                WGPUDeviceLostReason reason,
-                               char const* message,
+                               WGPUStringView message,
                                void* callback,
                                void* userdata) {
     if (callback == nullptr) {
@@ -182,7 +179,7 @@
 
 void LegacyUncapturedErrorCallback(WGPUDevice const*,
                                    WGPUErrorType type,
-                                   const char* message,
+                                   WGPUStringView message,
                                    void* callback,
                                    void* userdata) {
     if (callback == nullptr) {
@@ -212,11 +209,9 @@
 
     EventType GetType() override { return kType; }
 
-    WireResult ReadyHook(FutureID futureID, WGPUDeviceLostReason reason, const char* message) {
+    WireResult ReadyHook(FutureID futureID, WGPUDeviceLostReason reason, WGPUStringView message) {
         mReason = reason;
-        if (message != nullptr) {
-            mMessage = message;
-        }
+        mMessage = ToString(message);
         mDevice->mDeviceLostInfo.futureID = kNullFutureID;
         return WireResult::Success;
     }
@@ -234,8 +229,8 @@
         if (mDevice->mDeviceLostInfo.callback != nullptr) {
             const auto device =
                 mReason != WGPUDeviceLostReason_FailedCreation ? ToAPI(mDevice.Get()) : nullptr;
-            mDevice->mDeviceLostInfo.callback(
-                &device, mReason, mMessage ? mMessage->c_str() : nullptr, userdata1, userdata2);
+            mDevice->mDeviceLostInfo.callback(&device, mReason, ToOutputStringView(mMessage),
+                                              userdata1, userdata2);
         }
         mDevice->mUncapturedErrorCallbackInfo = kEmptyUncapturedErrorCallbackInfo;
     }
@@ -243,7 +238,7 @@
     WGPUDeviceLostReason mReason;
     // Note that the message is optional because we want to return nullptr when it wasn't set
     // instead of a pointer to an empty string.
-    std::optional<std::string> mMessage;
+    std::string mMessage;
 
     // Strong reference to the device so that when we call the callback we can pass the device.
     Ref<Device> mDevice;
@@ -258,7 +253,7 @@
 #if defined(DAWN_ENABLE_ASSERTS)
     static constexpr WGPUDeviceLostCallbackInfo2 kDefaultDeviceLostCallbackInfo = {
         nullptr, WGPUCallbackMode_AllowSpontaneous,
-        [](WGPUDevice const*, WGPUDeviceLostReason, char const*, void*, void*) {
+        [](WGPUDevice const*, WGPUDeviceLostReason, WGPUStringView, void*, void*) {
             static bool calledOnce = false;
             if (!calledOnce) {
                 calledOnce = true;
@@ -270,7 +265,7 @@
         nullptr, nullptr};
     static constexpr WGPUUncapturedErrorCallbackInfo2 kDefaultUncapturedErrorCallbackInfo = {
         nullptr,
-        [](WGPUDevice const*, WGPUErrorType, char const*, void*, void*) {
+        [](WGPUDevice const*, WGPUErrorType, WGPUStringView, void*, void*) {
             static bool calledOnce = false;
             if (!calledOnce) {
                 calledOnce = true;
@@ -328,7 +323,8 @@
 
 void Device::WillDropLastExternalRef() {
     if (IsRegistered()) {
-        HandleDeviceLost(WGPUDeviceLostReason_Destroyed, "Device was destroyed.");
+        HandleDeviceLost(WGPUDeviceLostReason_Destroyed,
+                         ToOutputStringView("Device was destroyed."));
     }
     Unregister();
 }
@@ -353,7 +349,7 @@
     return mLimitsAndFeatures.SetFeatures(features, featuresCount);
 }
 
-void Device::HandleError(WGPUErrorType errorType, const char* message) {
+void Device::HandleError(WGPUErrorType errorType, WGPUStringView message) {
     if (mUncapturedErrorCallbackInfo.callback) {
         const auto device = ToAPI(this);
         mUncapturedErrorCallbackInfo.callback(&device, errorType, message,
@@ -362,14 +358,14 @@
     }
 }
 
-void Device::HandleLogging(WGPULoggingType loggingType, const char* message) {
+void Device::HandleLogging(WGPULoggingType loggingType, WGPUStringView message) {
     if (mLoggingCallback) {
         // Since client always run in single thread, calling the callback directly is safe.
         mLoggingCallback(loggingType, message, mLoggingUserdata);
     }
 }
 
-void Device::HandleDeviceLost(WGPUDeviceLostReason reason, const char* message) {
+void Device::HandleDeviceLost(WGPUDeviceLostReason reason, WGPUStringView message) {
     FutureID futureID = GetDeviceLostFuture().id;
     if (futureID != kNullFutureID) {
         DAWN_CHECK(GetEventManager().SetFutureReady<DeviceLostEvent>(futureID, reason, message) ==
@@ -413,16 +409,16 @@
 WireResult Client::DoDeviceLostCallback(ObjectHandle eventManager,
                                         WGPUFuture future,
                                         WGPUDeviceLostReason reason,
-                                        char const* message) {
+                                        WGPUStringView message) {
     return GetEventManager(eventManager)
         .SetFutureReady<Device::DeviceLostEvent>(future.id, reason, message);
 }
 
 void Device::PopErrorScope(WGPUErrorCallback callback, void* userdata) {
-    static WGPUErrorCallback kDefaultCallback = [](WGPUErrorType, char const*, void*) {};
+    static WGPUErrorCallback kDefaultCallback = [](WGPUErrorType, WGPUStringView, void*) {};
 
     PopErrorScope2({nullptr, WGPUCallbackMode_AllowSpontaneous,
-                    [](WGPUPopErrorScopeStatus, WGPUErrorType type, char const* message,
+                    [](WGPUPopErrorScopeStatus, WGPUErrorType type, WGPUStringView message,
                        void* callback, void* userdata) {
                         auto cb = reinterpret_cast<WGPUErrorCallback>(callback);
                         cb(type, message, userdata);
@@ -434,7 +430,7 @@
 WGPUFuture Device::PopErrorScopeF(const WGPUPopErrorScopeCallbackInfo& callbackInfo) {
     return PopErrorScope2({callbackInfo.nextInChain, callbackInfo.mode,
                            [](WGPUPopErrorScopeStatus status, WGPUErrorType type,
-                              char const* message, void* callback, void* userdata) {
+                              WGPUStringView message, void* callback, void* userdata) {
                                auto cb = reinterpret_cast<WGPUPopErrorScopeCallback>(callback);
                                cb(status, type, message, userdata);
                            },
@@ -460,7 +456,7 @@
 WireResult Client::DoDevicePopErrorScopeCallback(ObjectHandle eventManager,
                                                  WGPUFuture future,
                                                  WGPUErrorType errorType,
-                                                 const char* message) {
+                                                 WGPUStringView message) {
     return GetEventManager(eventManager)
         .SetFutureReady<PopErrorScopeEvent>(future.id, errorType, message);
 }
@@ -537,7 +533,7 @@
     CreateComputePipelineAsync2(
         descriptor, {nullptr, WGPUCallbackMode_AllowSpontaneous,
                      [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline,
-                        char const* message, void* callback, void* userdata) {
+                        WGPUStringView message, void* callback, void* userdata) {
                          auto cb =
                              reinterpret_cast<WGPUCreateComputePipelineAsyncCallback>(callback);
                          cb(status, pipeline, message, userdata);
@@ -551,7 +547,7 @@
     return CreateComputePipelineAsync2(
         descriptor, {callbackInfo.nextInChain, callbackInfo.mode,
                      [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline,
-                        char const* message, void* callback, void* userdata) {
+                        WGPUStringView message, void* callback, void* userdata) {
                          auto cb =
                              reinterpret_cast<WGPUCreateComputePipelineAsyncCallback>(callback);
                          cb(status, pipeline, message, userdata);
@@ -569,7 +565,7 @@
 WireResult Client::DoDeviceCreateComputePipelineAsyncCallback(ObjectHandle eventManager,
                                                               WGPUFuture future,
                                                               WGPUCreatePipelineAsyncStatus status,
-                                                              const char* message) {
+                                                              WGPUStringView message) {
     return GetEventManager(eventManager)
         .SetFutureReady<CreateComputePipelineEvent>(future.id, status, message);
 }
@@ -580,7 +576,7 @@
     CreateRenderPipelineAsync2(
         descriptor, {nullptr, WGPUCallbackMode_AllowSpontaneous,
                      [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline,
-                        char const* message, void* callback, void* userdata) {
+                        WGPUStringView message, void* callback, void* userdata) {
                          auto cb =
                              reinterpret_cast<WGPUCreateRenderPipelineAsyncCallback>(callback);
                          cb(status, pipeline, message, userdata);
@@ -594,7 +590,7 @@
     return CreateRenderPipelineAsync2(
         descriptor, {callbackInfo.nextInChain, callbackInfo.mode,
                      [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline,
-                        char const* message, void* callback, void* userdata) {
+                        WGPUStringView message, void* callback, void* userdata) {
                          auto cb =
                              reinterpret_cast<WGPUCreateRenderPipelineAsyncCallback>(callback);
                          cb(status, pipeline, message, userdata);
@@ -612,7 +608,7 @@
 WireResult Client::DoDeviceCreateRenderPipelineAsyncCallback(ObjectHandle eventManager,
                                                              WGPUFuture future,
                                                              WGPUCreatePipelineAsyncStatus status,
-                                                             const char* message) {
+                                                             WGPUStringView message) {
     return GetEventManager(eventManager)
         .SetFutureReady<CreateRenderPipelineEvent>(future.id, status, message);
 }
diff --git a/src/dawn/wire/client/Device.h b/src/dawn/wire/client/Device.h
index 4106bb0..333c372 100644
--- a/src/dawn/wire/client/Device.h
+++ b/src/dawn/wire/client/Device.h
@@ -60,9 +60,9 @@
     bool IsAlive() const;
     WGPUFuture GetDeviceLostFuture();
 
-    void HandleError(WGPUErrorType errorType, const char* message);
-    void HandleLogging(WGPULoggingType loggingType, const char* message);
-    void HandleDeviceLost(WGPUDeviceLostReason reason, const char* message);
+    void HandleError(WGPUErrorType errorType, WGPUStringView message);
+    void HandleLogging(WGPULoggingType loggingType, WGPUStringView message);
+    void HandleDeviceLost(WGPUDeviceLostReason reason, WGPUStringView message);
     class DeviceLostEvent;
 
     // WebGPU API
diff --git a/src/dawn/wire/client/Instance.cpp b/src/dawn/wire/client/Instance.cpp
index a9dece8..19e940b 100644
--- a/src/dawn/wire/client/Instance.cpp
+++ b/src/dawn/wire/client/Instance.cpp
@@ -32,6 +32,7 @@
 #include <utility>
 
 #include "dawn/common/Log.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/common/WGSLFeatureMapping.h"
 #include "dawn/wire/client/ApiObjects_autogen.h"
 #include "dawn/wire/client/Client.h"
@@ -65,16 +66,14 @@
 
     WireResult ReadyHook(FutureID futureID,
                          WGPURequestAdapterStatus status,
-                         const char* message,
+                         WGPUStringView message,
                          const WGPUAdapterInfo* info,
                          const WGPUSupportedLimits* limits,
                          uint32_t featuresCount,
                          const WGPUFeatureName* features) {
         DAWN_ASSERT(mAdapter != nullptr);
         mStatus = status;
-        if (message != nullptr) {
-            mMessage = message;
-        }
+        mMessage = ToString(message);
         if (status == WGPURequestAdapterStatus_Success) {
             mAdapter->SetInfo(info);
             mAdapter->SetLimits(limits);
@@ -101,13 +100,13 @@
             mCallback(mStatus,
                       mStatus == WGPURequestAdapterStatus_Success ? ReturnToAPI(std::move(mAdapter))
                                                                   : nullptr,
-                      mMessage ? mMessage->c_str() : nullptr, mUserdata1.ExtractAsDangling());
+                      ToOutputStringView(mMessage), mUserdata1.ExtractAsDangling());
         } else {
             mCallback2(mStatus,
                        mStatus == WGPURequestAdapterStatus_Success
                            ? ReturnToAPI(std::move(mAdapter))
                            : nullptr,
-                       mMessage ? mMessage->c_str() : nullptr, mUserdata1.ExtractAsDangling(),
+                       ToOutputStringView(mMessage), mUserdata1.ExtractAsDangling(),
                        mUserdata2.ExtractAsDangling());
         }
     }
@@ -120,7 +119,7 @@
     // Note that the message is optional because we want to return nullptr when it wasn't set
     // instead of a pointer to an empty string.
     WGPURequestAdapterStatus mStatus;
-    std::optional<std::string> mMessage;
+    std::string mMessage;
 
     // The adapter is created when we call RequestAdapter(F). It is guaranteed to be alive
     // throughout the duration of a RequestAdapterEvent because the Event essentially takes
@@ -254,7 +253,7 @@
 WireResult Client::DoInstanceRequestAdapterCallback(ObjectHandle eventManager,
                                                     WGPUFuture future,
                                                     WGPURequestAdapterStatus status,
-                                                    const char* message,
+                                                    WGPUStringView message,
                                                     const WGPUAdapterInfo* info,
                                                     const WGPUSupportedLimits* limits,
                                                     uint32_t featuresCount,
diff --git a/src/dawn/wire/server/Server.cpp b/src/dawn/wire/server/Server.cpp
index 1859055..4c1d77c 100644
--- a/src/dawn/wire/server/Server.cpp
+++ b/src/dawn/wire/server/Server.cpp
@@ -174,7 +174,7 @@
     // Set callback to post warning and other information to client.
     mProcs.deviceSetLoggingCallback(
         device->handle,
-        [](WGPULoggingType type, const char* message, void* userdata) {
+        [](WGPULoggingType type, WGPUStringView message, void* userdata) {
             DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
             info->server->OnLogging(info->self, type, message);
         },
diff --git a/src/dawn/wire/server/Server.h b/src/dawn/wire/server/Server.h
index 179a8cd..d064f08 100644
--- a/src/dawn/wire/server/Server.h
+++ b/src/dawn/wire/server/Server.h
@@ -242,42 +242,42 @@
     void ClearDeviceCallbacks(WGPUDevice device);
 
     // Error callbacks
-    void OnUncapturedError(ObjectHandle device, WGPUErrorType type, const char* message);
-    void OnLogging(ObjectHandle device, WGPULoggingType type, const char* message);
+    void OnUncapturedError(ObjectHandle device, WGPUErrorType type, WGPUStringView message);
+    void OnLogging(ObjectHandle device, WGPULoggingType type, WGPUStringView message);
 
     // Async event callbacks
     void OnDeviceLost(DeviceLostUserdata* userdata,
                       WGPUDevice const* device,
                       WGPUDeviceLostReason reason,
-                      const char* message);
+                      WGPUStringView message);
     void OnDevicePopErrorScope(ErrorScopeUserdata* userdata,
                                WGPUPopErrorScopeStatus status,
                                WGPUErrorType type,
-                               const char* message);
+                               WGPUStringView message);
     void OnBufferMapAsyncCallback(MapUserdata* userdata, WGPUBufferMapAsyncStatus status);
     void OnBufferMapAsyncCallback2(MapUserdata* userdata,
                                    WGPUMapAsyncStatus status,
-                                   const char* message);
+                                   WGPUStringView message);
     void OnQueueWorkDone(QueueWorkDoneUserdata* userdata, WGPUQueueWorkDoneStatus status);
     void OnCreateComputePipelineAsyncCallback(CreatePipelineAsyncUserData* userdata,
                                               WGPUCreatePipelineAsyncStatus status,
                                               WGPUComputePipeline pipeline,
-                                              const char* message);
+                                              WGPUStringView message);
     void OnCreateRenderPipelineAsyncCallback(CreatePipelineAsyncUserData* userdata,
                                              WGPUCreatePipelineAsyncStatus status,
                                              WGPURenderPipeline pipeline,
-                                             const char* message);
+                                             WGPUStringView message);
     void OnShaderModuleGetCompilationInfo(ShaderModuleGetCompilationInfoUserdata* userdata,
                                           WGPUCompilationInfoRequestStatus status,
                                           const WGPUCompilationInfo* info);
     void OnRequestAdapterCallback(RequestAdapterUserdata* userdata,
                                   WGPURequestAdapterStatus status,
                                   WGPUAdapter adapter,
-                                  const char* message);
+                                  WGPUStringView message);
     void OnRequestDeviceCallback(RequestDeviceUserdata* userdata,
                                  WGPURequestDeviceStatus status,
                                  WGPUDevice device,
-                                 const char* message);
+                                 WGPUStringView message);
 
 #include "dawn/wire/server/ServerPrototypes_autogen.inc"
 
diff --git a/src/dawn/wire/server/ServerAdapter.cpp b/src/dawn/wire/server/ServerAdapter.cpp
index 1267930..8d4c3c8 100644
--- a/src/dawn/wire/server/ServerAdapter.cpp
+++ b/src/dawn/wire/server/ServerAdapter.cpp
@@ -27,6 +27,7 @@
 
 #include <vector>
 
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/wire/SupportedFeatures.h"
 #include "dawn/wire/WireResult.h"
 #include "dawn/wire/server/ObjectStorage.h"
@@ -61,7 +62,7 @@
                                     deviceLostUserdata.release(), nullptr};
     desc.uncapturedErrorCallbackInfo2 = {
         nullptr,
-        [](WGPUDevice const*, WGPUErrorType type, const char* message, void*, void* userdata) {
+        [](WGPUDevice const*, WGPUErrorType type, WGPUStringView message, void*, void* userdata) {
             DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
             info->server->OnUncapturedError(info->self, type, message);
         },
@@ -83,7 +84,7 @@
 void Server::OnRequestDeviceCallback(RequestDeviceUserdata* data,
                                      WGPURequestDeviceStatus status,
                                      WGPUDevice device,
-                                     const char* message) {
+                                     WGPUStringView message) {
     ReturnAdapterRequestDeviceCallbackCmd cmd = {};
     cmd.eventManager = data->eventManager;
     cmd.future = data->future;
@@ -113,7 +114,7 @@
             device = nullptr;
 
             cmd.status = WGPURequestDeviceStatus_Error;
-            cmd.message = "Requested feature not supported.";
+            cmd.message = ToOutputStringView("Requested feature not supported.");
             SerializeCommand(cmd);
             return;
         }
@@ -143,7 +144,7 @@
     Known<WGPUDevice> reservation;
     if (FillReservation(data->deviceObjectId, device, &reservation) == WireResult::FatalError) {
         cmd.status = WGPURequestDeviceStatus_Unknown;
-        cmd.message = "Destroyed before request was fulfilled.";
+        cmd.message = ToOutputStringView("Destroyed before request was fulfilled.");
         SerializeCommand(cmd);
         return;
     }
diff --git a/src/dawn/wire/server/ServerBuffer.cpp b/src/dawn/wire/server/ServerBuffer.cpp
index 233fc03..09f6a6d 100644
--- a/src/dawn/wire/server/ServerBuffer.cpp
+++ b/src/dawn/wire/server/ServerBuffer.cpp
@@ -29,6 +29,7 @@
 #include <memory>
 
 #include "dawn/common/Assert.h"
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/wire/BufferConsumer_impl.h"
 #include "dawn/wire/WireCmd_autogen.h"
 #include "dawn/wire/WireResult.h"
@@ -239,7 +240,7 @@
     cmd.eventManager = data->eventManager;
     cmd.future = data->future;
     cmd.status = status;
-    cmd.message = nullptr;
+    cmd.message = kEmptyOutputStringView;
     cmd.readDataUpdateInfoLength = 0;
     cmd.readDataUpdateInfo = nullptr;
     cmd.userdataCount = 1;
@@ -282,7 +283,7 @@
 
 void Server::OnBufferMapAsyncCallback2(MapUserdata* data,
                                        WGPUMapAsyncStatus status,
-                                       const char* message) {
+                                       WGPUStringView message) {
     // Skip sending the callback if the buffer has already been destroyed.
     Known<WGPUBuffer> buffer;
     if (Objects<WGPUBuffer>().Get(data->buffer.id, &buffer) != WireResult::Success ||
diff --git a/src/dawn/wire/server/ServerDevice.cpp b/src/dawn/wire/server/ServerDevice.cpp
index 7cfe9ef..f428677 100644
--- a/src/dawn/wire/server/ServerDevice.cpp
+++ b/src/dawn/wire/server/ServerDevice.cpp
@@ -27,12 +27,13 @@
 
 #include "dawn/wire/server/Server.h"
 
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/wire/Wire.h"
 #include "dawn/wire/WireResult.h"
 
 namespace dawn::wire::server {
 
-void Server::OnUncapturedError(ObjectHandle device, WGPUErrorType type, const char* message) {
+void Server::OnUncapturedError(ObjectHandle device, WGPUErrorType type, WGPUStringView message) {
     ReturnDeviceUncapturedErrorCallbackCmd cmd;
     cmd.device = device;
     cmd.type = type;
@@ -44,7 +45,7 @@
 void Server::OnDeviceLost(DeviceLostUserdata* userdata,
                           WGPUDevice const* device,
                           WGPUDeviceLostReason reason,
-                          const char* message) {
+                          WGPUStringView message) {
     ReturnDeviceLostCallbackCmd cmd;
     cmd.eventManager = userdata->eventManager;
     cmd.future = userdata->future;
@@ -54,7 +55,7 @@
     SerializeCommand(cmd);
 }
 
-void Server::OnLogging(ObjectHandle device, WGPULoggingType type, const char* message) {
+void Server::OnLogging(ObjectHandle device, WGPULoggingType type, WGPUStringView message) {
     ReturnDeviceLoggingCallbackCmd cmd;
     cmd.device = device;
     cmd.type = type;
@@ -80,7 +81,7 @@
 void Server::OnDevicePopErrorScope(ErrorScopeUserdata* userdata,
                                    WGPUPopErrorScopeStatus status,
                                    WGPUErrorType type,
-                                   const char* message) {
+                                   WGPUStringView message) {
     ReturnDevicePopErrorScopeCallbackCmd cmd;
     cmd.eventManager = userdata->eventManager;
     cmd.future = userdata->future;
@@ -117,7 +118,7 @@
 void Server::OnCreateComputePipelineAsyncCallback(CreatePipelineAsyncUserData* data,
                                                   WGPUCreatePipelineAsyncStatus status,
                                                   WGPUComputePipeline pipeline,
-                                                  const char* message) {
+                                                  WGPUStringView message) {
     ReturnDeviceCreateComputePipelineAsyncCallbackCmd cmd;
     cmd.eventManager = data->eventManager;
     cmd.future = data->future;
@@ -127,7 +128,7 @@
     if (status == WGPUCreatePipelineAsyncStatus_Success &&
         FillReservation(data->pipelineObjectID, pipeline) == WireResult::FatalError) {
         cmd.status = WGPUCreatePipelineAsyncStatus_Unknown;
-        cmd.message = "Destroyed before request was fulfilled.";
+        cmd.message = ToOutputStringView("Destroyed before request was fulfilled.");
     }
     SerializeCommand(cmd);
 }
@@ -159,7 +160,7 @@
 void Server::OnCreateRenderPipelineAsyncCallback(CreatePipelineAsyncUserData* data,
                                                  WGPUCreatePipelineAsyncStatus status,
                                                  WGPURenderPipeline pipeline,
-                                                 const char* message) {
+                                                 WGPUStringView message) {
     ReturnDeviceCreateRenderPipelineAsyncCallbackCmd cmd;
     cmd.eventManager = data->eventManager;
     cmd.future = data->future;
@@ -169,7 +170,7 @@
     if (status == WGPUCreatePipelineAsyncStatus_Success &&
         FillReservation(data->pipelineObjectID, pipeline) == WireResult::FatalError) {
         cmd.status = WGPUCreatePipelineAsyncStatus_Unknown;
-        cmd.message = "Destroyed before request was fulfilled.";
+        cmd.message = ToOutputStringView("Destroyed before request was fulfilled.");
     }
     SerializeCommand(cmd);
 }
diff --git a/src/dawn/wire/server/ServerInstance.cpp b/src/dawn/wire/server/ServerInstance.cpp
index ca87d8c..4e1b883 100644
--- a/src/dawn/wire/server/ServerInstance.cpp
+++ b/src/dawn/wire/server/ServerInstance.cpp
@@ -28,6 +28,7 @@
 #include <algorithm>
 #include <vector>
 
+#include "dawn/common/StringViewUtils.h"
 #include "dawn/wire/SupportedFeatures.h"
 #include "dawn/wire/server/ObjectStorage.h"
 #include "dawn/wire/server/Server.h"
@@ -64,7 +65,7 @@
 void Server::OnRequestAdapterCallback(RequestAdapterUserdata* data,
                                       WGPURequestAdapterStatus status,
                                       WGPUAdapter adapter,
-                                      const char* message) {
+                                      WGPUStringView message) {
     ReturnInstanceRequestAdapterCallbackCmd cmd = {};
     cmd.eventManager = data->eventManager;
     cmd.future = data->future;
@@ -80,7 +81,7 @@
     // Assign the handle and allocated status if the adapter is created successfully.
     if (FillReservation(data->adapterObjectId, adapter) == WireResult::FatalError) {
         cmd.status = WGPURequestAdapterStatus_Unknown;
-        cmd.message = "Destroyed before request was fulfilled.";
+        cmd.message = ToOutputStringView("Destroyed before request was fulfilled.");
         SerializeCommand(cmd);
         return;
     }