Skip to content

Commit fe6d24d

Browse files
authored
improve error reporting for fatal errors (vercel/turborepo#3550)
This adds the turbo-tasks function name as `context` of all fatal errors propagated from executing or reading turbo-tasks functions. That should help in debugging fatal errors.
1 parent d2d707c commit fe6d24d

File tree

21 files changed

+197
-105
lines changed

21 files changed

+197
-105
lines changed

crates/next-core/src/next_client_chunks/with_chunks.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ impl EcmascriptChunkItem for WithChunksChunkItem {
105105
self.context
106106
}
107107

108+
#[turbo_tasks::function]
109+
fn related_path(&self) -> FileSystemPathVc {
110+
self.inner.path()
111+
}
112+
108113
#[turbo_tasks::function]
109114
async fn content(&self) -> Result<EcmascriptChunkItemContentVc> {
110115
let inner = self.inner.await?;

crates/next-core/src/next_client_component/with_client_chunks.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ impl EcmascriptChunkItem for WithClientChunksChunkItem {
105105
self.context
106106
}
107107

108+
#[turbo_tasks::function]
109+
fn related_path(&self) -> FileSystemPathVc {
110+
self.inner.path()
111+
}
112+
108113
#[turbo_tasks::function]
109114
async fn content(&self) -> Result<EcmascriptChunkItemContentVc> {
110115
let inner = self.inner.await?;

crates/turbo-tasks-memory/src/output.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl Output {
5252
pub fn read_untracked(&mut self) -> Result<RawVc> {
5353
match &self.content {
5454
OutputContent::Empty => Err(anyhow!("Output is empty")),
55-
OutputContent::Error(err) => Err(err.clone().into()),
55+
OutputContent::Error(err) => Err(anyhow::Error::new(err.clone())),
5656
OutputContent::Link(raw_vc) => Ok(*raw_vc),
5757
OutputContent::Panic(Some(message)) => Err(anyhow!("A task panicked: {message}")),
5858
OutputContent::Panic(None) => Err(anyhow!("A task panicked")),

crates/turbo-tasks-memory/src/task.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,19 @@ impl Task {
440440
}
441441
}
442442

443+
pub(crate) fn get_function_name(&self) -> Option<&'static str> {
444+
if let TaskType::Persistent(ty) = &self.ty {
445+
match &**ty {
446+
PersistentTaskType::Native(native_fn, _)
447+
| PersistentTaskType::ResolveNative(native_fn, _) => {
448+
return Some(&registry::get_function(*native_fn).name);
449+
}
450+
_ => {}
451+
}
452+
}
453+
None
454+
}
455+
443456
pub(crate) fn get_description(&self) -> String {
444457
match &self.ty {
445458
TaskType::Root(..) => format!("[{}] root", self.id),
@@ -700,7 +713,12 @@ impl Task {
700713
match state.state_type {
701714
InProgress { .. } => match result {
702715
Ok(Ok(result)) => state.output.link(result, turbo_tasks),
703-
Ok(Err(err)) => state.output.error(err, turbo_tasks),
716+
Ok(Err(mut err)) => {
717+
if let Some(name) = self.get_function_name() {
718+
err = err.context(format!("Execution of {} failed", name));
719+
}
720+
state.output.error(err, turbo_tasks)
721+
}
704722
Err(message) => state.output.panic(message, turbo_tasks),
705723
},
706724
InProgressDirty { .. } => {

crates/turbo-tasks/src/native_function.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
sync::atomic::{AtomicUsize, Ordering},
77
};
88

9-
use anyhow::Result;
9+
use anyhow::{Context, Result};
1010

1111
use crate::{
1212
self as turbo_tasks, registry::register_function, task_input::TaskInput, util::SharedError,
@@ -56,7 +56,9 @@ impl NativeFunction {
5656

5757
/// Creates a functor for execution from a fixed set of inputs.
5858
pub fn bind(&'static self, inputs: &Vec<TaskInput>) -> NativeTaskFn {
59-
match (self.bind_fn)(inputs) {
59+
match (self.bind_fn)(inputs)
60+
.with_context(|| format!("Error during argument binding of {}", self.name))
61+
{
6062
Ok(native_fn) => Box::new(move || {
6163
let r = native_fn();
6264
if cfg!(feature = "log_function_stats") {

crates/turbo-tasks/src/util.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@ pub struct SharedError {
1717

1818
impl SharedError {
1919
pub fn new(err: Error) -> Self {
20-
match err.downcast::<SharedError>() {
21-
Ok(shared) => shared,
22-
Err(plain) => Self {
23-
inner: Arc::new(plain),
24-
},
20+
Self {
21+
inner: Arc::new(err),
2522
}
2623
}
2724
}

crates/turbopack-core/src/chunk/mod.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
pub mod dev;
22
pub mod optimize;
33

4-
use std::{collections::VecDeque, fmt::Debug};
4+
use std::{
5+
collections::VecDeque,
6+
fmt::{Debug, Display},
7+
};
58

69
use anyhow::{anyhow, Result};
710
use indexmap::IndexSet;
@@ -32,6 +35,23 @@ pub enum ModuleId {
3235
String(String),
3336
}
3437

38+
impl Display for ModuleId {
39+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40+
match self {
41+
ModuleId::Number(i) => write!(f, "{}", i),
42+
ModuleId::String(s) => write!(f, "{}", s),
43+
}
44+
}
45+
}
46+
47+
#[turbo_tasks::value_impl]
48+
impl ValueToString for ModuleId {
49+
#[turbo_tasks::function]
50+
fn to_string(&self) -> StringVc {
51+
StringVc::cell(self.to_string())
52+
}
53+
}
54+
3555
impl ModuleId {
3656
pub fn parse(id: &str) -> Result<ModuleId> {
3757
Ok(match id.parse::<u32>() {

crates/turbopack-css/src/module_asset.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ impl EcmascriptChunkItem for ModuleChunkItem {
173173
self.context
174174
}
175175

176+
#[turbo_tasks::function]
177+
fn related_path(&self) -> FileSystemPathVc {
178+
self.module.path()
179+
}
180+
176181
#[turbo_tasks::function]
177182
async fn content(&self) -> Result<EcmascriptChunkItemContentVc> {
178183
let parsed = self.module.parse().await?;

crates/turbopack-ecmascript/src/chunk/mod.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use turbopack_core::{
3232
asset::{children_from_asset_references, content_to_details, IntrospectableAssetVc},
3333
Introspectable, IntrospectableChildrenVc, IntrospectableVc,
3434
},
35+
issue::{code_gen::CodeGenerationIssue, IssueSeverity},
3536
reference::{AssetReferenceVc, AssetReferencesVc},
3637
source_map::{GenerateSourceMap, GenerateSourceMapVc, OptionSourceMapVc, SourceMapVc},
3738
version::{
@@ -490,7 +491,36 @@ impl EcmascriptChunkContentEntryVc {
490491
#[turbo_tasks::function]
491492
async fn new(chunk_item: EcmascriptChunkItemVc) -> Result<Self> {
492493
let content = chunk_item.content();
493-
let factory = module_factory(content);
494+
let factory = match module_factory(content).resolve().await {
495+
Ok(factory) => factory,
496+
Err(error) => {
497+
let id = chunk_item.id().to_string().await;
498+
let id = id.as_ref().map_or_else(|_| "unknown", |id| &**id);
499+
let mut error_message =
500+
format!("An error occurred while generating the chunk item {}", id);
501+
for err in error.chain() {
502+
write!(error_message, "\n at {}", err)?;
503+
}
504+
let js_error_message = serde_json::to_string(&error_message)?;
505+
let issue = CodeGenerationIssue {
506+
severity: IssueSeverity::Error.cell(),
507+
path: chunk_item.related_path(),
508+
title: StringVc::cell("Code generation for chunk item errored".to_string()),
509+
message: StringVc::cell(error_message),
510+
}
511+
.cell();
512+
issue.as_issue().emit();
513+
let mut code = CodeBuilder::default();
514+
code += "(() => {{\n\n";
515+
write!(
516+
code,
517+
"throw new Error({error});\n",
518+
error = &js_error_message
519+
)?;
520+
code += "\n}})";
521+
code.build().cell()
522+
}
523+
};
494524
let id = chunk_item.id().await?;
495525
let code = factory.await?;
496526
let hash = hash_xxh3_hash64(code.source_code());
@@ -1196,6 +1226,7 @@ pub struct EcmascriptChunkItemOptions {
11961226

11971227
#[turbo_tasks::value_trait]
11981228
pub trait EcmascriptChunkItem: ChunkItem + ValueToString {
1229+
fn related_path(&self) -> FileSystemPathVc;
11991230
fn content(&self) -> EcmascriptChunkItemContentVc;
12001231
fn chunking_context(&self) -> ChunkingContextVc;
12011232
fn id(&self) -> ModuleIdVc {

crates/turbopack-ecmascript/src/chunk_group_files_asset.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ impl EcmascriptChunkItem for ChunkGroupFilesChunkItem {
118118
self.context
119119
}
120120

121+
#[turbo_tasks::function]
122+
fn related_path(&self) -> FileSystemPathVc {
123+
self.inner.path()
124+
}
125+
121126
#[turbo_tasks::function]
122127
async fn content(&self) -> Result<EcmascriptChunkItemContentVc> {
123128
let chunks = self.inner.chunks();

0 commit comments

Comments
 (0)