diff --git a/onnxruntime/core/providers/openvino/openvino_execution_provider.cc b/onnxruntime/core/providers/openvino/openvino_execution_provider.cc index 049af81c9ffb2..1504f023502aa 100644 --- a/onnxruntime/core/providers/openvino/openvino_execution_provider.cc +++ b/onnxruntime/core/providers/openvino/openvino_execution_provider.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include "core/providers/shared_library/provider_api.h" #include "core/providers/openvino/openvino_execution_provider.h" #include "core/providers/openvino/contexts.h" @@ -170,12 +171,19 @@ common::Status OpenVINOExecutionProvider::Compile( return 0; }; - compute_info.compute_func = [](FunctionState state, const OrtApi* /* api */, OrtKernelContext* context) { + compute_info.compute_func = [](FunctionState state, const OrtApi* /* api */, OrtKernelContext* context) -> Status { auto function_state = static_cast(state); try { function_state->backend_manager.Compute(context); } catch (const std::exception& ex) { - return common::Status(common::ONNXRUNTIME, common::FAIL, ex.what()); + // Extract error code from message with pattern: "|message" + auto get_error_code = [](std::string_view msg) { + if (auto pos = msg.find('|'); pos != std::string_view::npos) + if (int code = 0; std::from_chars(msg.data(), msg.data() + pos, code).ec == std::errc{}) + return code; + return static_cast(common::EP_FAIL); + }; + return common::Status(common::ONNXRUNTIME, get_error_code(ex.what()), ex.what()); } return Status::OK(); }; diff --git a/onnxruntime/core/providers/openvino/ov_interface.cc b/onnxruntime/core/providers/openvino/ov_interface.cc index e97bbaceee4e2..39c7701c96997 100644 --- a/onnxruntime/core/providers/openvino/ov_interface.cc +++ b/onnxruntime/core/providers/openvino/ov_interface.cc @@ -3,7 +3,9 @@ #include "core/providers/openvino/ov_interface.h" +#include #include +#include #define ORT_API_MANUAL_INIT #include "core/session/onnxruntime_cxx_api.h" @@ -16,14 +18,75 @@ namespace onnxruntime { namespace openvino_ep { +namespace { +// Helper to extract Level Zero error codes from NPU driver exceptions +struct ZEErrorInfo { + uint32_t code{0}; + std::string name{"UNKNOWN_NPU_ERROR"}; + + static ZEErrorInfo ExtractFromMessage(std::string_view ov_exception_msg) { + ZEErrorInfo info; + + // Extract hex error code: "code 0x([0-9a-fA-F]+)" + std::regex code_pattern("code 0x([0-9a-fA-F]+)"); + std::string msg_str(ov_exception_msg); + std::smatch matches; + if (std::regex_search(msg_str, matches, code_pattern) && matches.size() > 1) { + const auto& code_str = matches[1].str(); + std::from_chars(code_str.data(), code_str.data() + code_str.size(), info.code, 16); + } + + // Extract ZE error name: R"(\bZE_\w*\b)" + std::regex name_pattern(R"(\bZE_\w*\b)"); + if (std::regex_search(msg_str, matches, name_pattern)) { + info.name = matches[0]; + } + + return info; + } + + // Check if this is ZE_RESULT_ERROR_INVALID_NATIVE_BINARY (driver/binary mismatch) + bool IsInvalidNativeBinary() const { + return code == 0x7800000f; + } +}; +} // namespace + template inline auto OvExceptionBoundary(Func&& func, std::format_string&& fmt, Args&&... args) { + return OvExceptionBoundary(func, ORT_EP_FAIL, std::forward>(fmt), std::forward(args)...); +} + +template +inline auto OvExceptionBoundary(Func&& func, OrtErrorCode error_code, std::format_string&& fmt, Args&&... args) { try { return func(); } catch (const ov::Exception& e) { - ORT_THROW(log_tag + std::vformat(fmt.get(), std::make_format_args(args...)) + ": " + std::string(e.what())); + std::string ov_msg = e.what(); + + // For import failures, extract and report ZE error codes for better NPU diagnostics + if (error_code == ORT_INVALID_GRAPH) { + auto ze_info = ZEErrorInfo::ExtractFromMessage(ov_msg); + if (ze_info.IsInvalidNativeBinary()) { + // Enhance message for binary incompatibility (most common import failure) + ov_msg = std::format("{}, code 0x{:x}\nModel needs to be recompiled\n", + ze_info.name, ze_info.code); + } else if (ze_info.code != 0) { + // Include ZE error details for other NPU failures + ov_msg = std::format("{}, code 0x{:x}: {}", + ze_info.name, ze_info.code, ov_msg); + } + } + + // Encode error code as prefix: "|message" + auto msg = std::format("{}|{}{}: {}", static_cast(error_code), log_tag, + std::vformat(fmt.get(), std::make_format_args(args...)), ov_msg); + ORT_THROW(msg); } catch (...) { - ORT_THROW(log_tag + std::vformat(fmt.get(), std::make_format_args(args...))); + // Encode error code as prefix: "|message" + auto msg = std::format("{}|{}{}", static_cast(error_code), log_tag, + std::vformat(fmt.get(), std::make_format_args(args...))); + ORT_THROW(msg); } } @@ -214,7 +277,8 @@ OVExeNetwork OVCore::ImportModel(ModelBlobWrapper& model_blob, #endif return exe; }, - "Exception while Loading Network for graph {}", name); + ORT_INVALID_GRAPH, + "Exception while importing model for graph {}", name); } OVExeNetwork OVCore::ImportEPCtxOVIREncapsulation(std::istream& model_stream,