mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-21 16:02:28 +00:00
fix: improve error display of nativets exceptions
This commit is contained in:
@@ -66,6 +66,8 @@ pub enum Error {
|
||||
DatabaseMigration(#[from] MigrateError),
|
||||
#[error("Non-zero exit status for {0}: {1}")]
|
||||
ExitStatus(String, i32),
|
||||
#[error("ExecutionRawError: {0}")]
|
||||
ExecutionRawError(Box<serde_json::value::RawValue>),
|
||||
#[error("Error: {error:#} @{location:#}")]
|
||||
Anyhow { error: anyhow::Error, location: String },
|
||||
#[error("Error: {0:#?}")]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use futures::{stream, TryStreamExt};
|
||||
use serde_json::{json, value::RawValue};
|
||||
use sqlx::types::Json;
|
||||
@@ -134,11 +133,13 @@ pub async fn do_graphql(
|
||||
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
|
||||
|
||||
if let Some(errors) = result.errors {
|
||||
return Err(anyhow!(errors
|
||||
.into_iter()
|
||||
.map(|x| x.message)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),));
|
||||
return Err(Error::ExecutionErr(
|
||||
errors
|
||||
.into_iter()
|
||||
.map(|x| x.message)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
));
|
||||
}
|
||||
|
||||
// And then check that we got back the same string we sent over.
|
||||
|
||||
@@ -526,7 +526,7 @@ pub async fn run_future_with_polling_update_job_poller<Fut, T, S>(
|
||||
get_mem: S,
|
||||
) -> error::Result<T>
|
||||
where
|
||||
Fut: Future<Output = anyhow::Result<T>>,
|
||||
Fut: Future<Output = windmill_common::error::Result<T>>,
|
||||
S: stream::Stream<Item = i32> + Unpin,
|
||||
{
|
||||
let (tx, rx) = broadcast::channel::<()>(3);
|
||||
|
||||
@@ -789,7 +789,7 @@ pub async fn eval_fetch_timeout(
|
||||
w_id: &str,
|
||||
load_client: bool,
|
||||
occupation_metrics: &mut OccupancyMetrics,
|
||||
) -> anyhow::Result<Box<RawValue>> {
|
||||
) -> windmill_common::error::Result<Box<RawValue>> {
|
||||
use windmill_queue::append_logs;
|
||||
|
||||
let (sender, mut receiver) = oneshot::channel::<IsolateHandle>();
|
||||
@@ -933,7 +933,7 @@ pub async fn eval_fetch_timeout(
|
||||
let r = runtime.block_on(future)?;
|
||||
// tracing::info!("total: {:?}", instant.elapsed());
|
||||
|
||||
r
|
||||
r as windmill_common::error::Result<Box<RawValue>>
|
||||
});
|
||||
|
||||
let res = run_future_with_polling_update_job_poller(
|
||||
@@ -942,7 +942,7 @@ pub async fn eval_fetch_timeout(
|
||||
conn,
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
async { result_f.await? },
|
||||
async { result_f.await.map_err(windmill_common::error::to_anyhow)? },
|
||||
worker_name,
|
||||
w_id,
|
||||
&mut Some(occupation_metrics),
|
||||
@@ -1004,22 +1004,26 @@ async fn eval_fetch(
|
||||
script_entrypoint_override: Option<String>,
|
||||
load_client: bool,
|
||||
job_id: &Uuid,
|
||||
) -> anyhow::Result<Box<RawValue>> {
|
||||
) -> windmill_common::error::Result<Box<RawValue>> {
|
||||
if load_client {
|
||||
if let Some(env_code) = env_code.as_ref() {
|
||||
let _ = js_runtime
|
||||
.load_side_es_module_from_code(
|
||||
&deno_core::resolve_url("file:///windmill.ts")?,
|
||||
&deno_core::resolve_url("file:///windmill.ts").map_err(error::to_anyhow)?,
|
||||
format!("{env_code}\n{}", WINDMILL_CLIENT.to_string()),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(error::to_anyhow)?;
|
||||
}
|
||||
}
|
||||
use anyhow::Context;
|
||||
use deno_core::error::CoreError;
|
||||
use windmill_common::{error, worker::to_raw_value};
|
||||
let source = format!("{}\n{expr}", env_code.unwrap_or_default());
|
||||
let _ = js_runtime
|
||||
.load_side_es_module_from_code(
|
||||
&deno_core::resolve_url("file:///eval.ts")?,
|
||||
format!("{}\n{expr}", env_code.unwrap_or_default()),
|
||||
&deno_core::resolve_url("file:///eval.ts").map_err(error::to_anyhow)?,
|
||||
source.to_string(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
@@ -1052,15 +1056,50 @@ import("file:///eval.ts").then((module) => module.{main_override}(...args)).then
|
||||
.map_err(|e| {
|
||||
write_error_expr(expr, &job_id);
|
||||
e
|
||||
})
|
||||
.context("native script event loop")?;
|
||||
});
|
||||
|
||||
let scope = &mut js_runtime.handle_scope();
|
||||
let local = v8::Local::new(scope, global);
|
||||
// Deserialize a `v8` object into a Rust type using `serde_v8`,
|
||||
// in this case deserialize to a JSON `Value`.
|
||||
let r = serde_v8::from_v8::<Option<String>>(scope, local)?;
|
||||
Ok(unsafe_raw(r.unwrap_or_else(|| "null".to_string())))
|
||||
match global {
|
||||
Ok(global) => {
|
||||
let scope = &mut js_runtime.handle_scope();
|
||||
let local = v8::Local::new(scope, global);
|
||||
// Deserialize a `v8` object into a Rust type using `serde_v8`,
|
||||
// in this case deserialize to a JSON `Value`.
|
||||
let r = serde_v8::from_v8::<Option<String>>(scope, local).map_err(error::to_anyhow)?;
|
||||
Ok(unsafe_raw(r.unwrap_or_else(|| "null".to_string())))
|
||||
}
|
||||
Err(CoreError::Js(e)) => {
|
||||
let stack_head = e.frames.first().and_then(|f| {
|
||||
if f.file_name.as_ref().is_some_and(|x| x == "file:///eval.ts") {
|
||||
Some(format!(
|
||||
"{}\n",
|
||||
source
|
||||
.lines()
|
||||
.nth((f.line_number.unwrap_or(1)) as usize - 1)
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let stack_s = format!(
|
||||
"{}{}",
|
||||
stack_head.unwrap_or("".to_string()),
|
||||
e.stack.unwrap_or("".to_string())
|
||||
);
|
||||
let stack = if stack_s.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(stack_s)
|
||||
};
|
||||
Err(Error::ExecutionRawError(to_raw_value(&serde_json::json!({
|
||||
"message": e.message,
|
||||
"stack": stack,
|
||||
"name": e.name,
|
||||
}))))
|
||||
}
|
||||
Err(e) => Err(Error::ExecutionErr(e.print_with_cause())),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "deno_core")]
|
||||
|
||||
@@ -68,7 +68,7 @@ fn do_postgresql_inner<'a>(
|
||||
column_order: Option<&'a mut Option<Vec<String>>>,
|
||||
siz: &'a AtomicUsize,
|
||||
skip_collect: bool,
|
||||
) -> error::Result<BoxFuture<'a, anyhow::Result<Box<RawValue>>>> {
|
||||
) -> error::Result<BoxFuture<'a, error::Result<Box<RawValue>>>> {
|
||||
let mut query_params = vec![];
|
||||
|
||||
let arg_indices = parse_pg_statement_arg_indices(&query);
|
||||
@@ -136,17 +136,17 @@ fn do_postgresql_inner<'a>(
|
||||
if *CLOUD_HOSTED {
|
||||
let siz = siz.load(Ordering::Relaxed);
|
||||
if siz > MAX_RESULT_SIZE * 4 {
|
||||
return Err(anyhow::anyhow!(
|
||||
return Err(Error::ExecutionErr(format!(
|
||||
"Query result too large for cloud (size = {} > {})",
|
||||
siz,
|
||||
MAX_RESULT_SIZE & 4
|
||||
));
|
||||
MAX_RESULT_SIZE & 4,
|
||||
)));
|
||||
}
|
||||
}
|
||||
if let Ok(v) = r {
|
||||
res.push(v);
|
||||
} else {
|
||||
return Err(to_anyhow(r.err().unwrap()));
|
||||
return Err(to_anyhow(r.err().unwrap()).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,6 +373,7 @@ pub async fn process_result(
|
||||
}
|
||||
}
|
||||
}
|
||||
Error::ExecutionRawError(e) => to_raw_value(&e),
|
||||
err @ _ => to_raw_value(&SerializedError {
|
||||
message: format!("execution error:\n{err:#}",),
|
||||
name: "ExecutionErr".to_string(),
|
||||
|
||||
@@ -2,13 +2,12 @@ use base64::{engine, Engine as _};
|
||||
use chrono::Datelike;
|
||||
use core::fmt::Write;
|
||||
use futures::future::BoxFuture;
|
||||
use futures::{FutureExt, TryFutureExt};
|
||||
use futures::FutureExt;
|
||||
use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
|
||||
use reqwest::{Client, Response};
|
||||
use serde_json::{json, value::RawValue, Value};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::HashMap;
|
||||
use windmill_common::error::to_anyhow;
|
||||
use windmill_common::worker::Connection;
|
||||
|
||||
use windmill_common::{error::Error, worker::to_raw_value};
|
||||
@@ -428,7 +427,7 @@ pub async fn do_snowflake(
|
||||
conn,
|
||||
mem_peak,
|
||||
canceled_by,
|
||||
result_f.map_err(to_anyhow),
|
||||
result_f,
|
||||
worker_name,
|
||||
&job.workspace_id,
|
||||
&mut Some(occupancy_metrics),
|
||||
|
||||
Reference in New Issue
Block a user