Skip to content

Commit

Permalink
docs(errors): improve error message for recursive calls (#9650)
Browse files Browse the repository at this point in the history
Closes #9224 

### Description

This is a state that you can get into two ways:
- Genuinely configuring their way into a loop with misconfigured scripts
and tasks
- Accidentally misconfiguring their package manager Workspace
- In some misconfigurations, the Workspace will look like a
single-package Workspace, but the previous iteration of our error
message didn't give any hints about this.

Either way, we should be providing an informative error message, so this
PR provides a message that caters to both situations. We'll need a more
verbose explanation, so an error link has been added here, as well.

### Testing Instructions

Delete `pnpm-workspace.yaml` from `create-turbo`, remove `turbo` from
`devDependencies`, re-run package manager installation, and try to run
`turbo build`. You should be greeted by this new and improved error
message.

---------

Co-authored-by: Chris Olszewski <[email protected]>
  • Loading branch information
anthonyshew and chris-olszewski authored Jan 2, 2025
1 parent 1a94894 commit 7a92d0b
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 6 deletions.
17 changes: 14 additions & 3 deletions crates/turborepo-lib/src/task_graph/visitor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ mod command;
mod error;
mod exec;
mod output;

use std::{
borrow::Cow,
collections::HashSet,
Expand All @@ -11,6 +10,7 @@ use std::{
};

use console::{Style, StyledObject};
use convert_case::{Case, Casing};
use error::{TaskError, TaskWarning};
use exec::ExecContextFactory;
use futures::{stream::FuturesUnordered, StreamExt};
Expand All @@ -23,6 +23,7 @@ use tracing::{debug, error, warn, Span};
use turbopath::{AbsoluteSystemPath, AnchoredSystemPath};
use turborepo_ci::{Vendor, VendorBehavior};
use turborepo_env::{platform::PlatformEnv, EnvironmentVariableMap};
use turborepo_errors::TURBO_SITE;
use turborepo_repository::package_graph::{PackageGraph, PackageName, ROOT_PKG_NAME};
use turborepo_telemetry::events::{
generic::GenericEventBuilder, task::PackageTaskEventBuilder, EventBuilder, TrackedErrors,
Expand Down Expand Up @@ -77,12 +78,21 @@ pub enum Error {
task_id: TaskId<'static>,
},
#[error(
"root task {task_name} ({command}) looks like it invokes turbo and might cause a loop"
"Your `package.json` script looks like it invokes a Root Task ({task_name}), creating a \
loop of `turbo` invocations. You likely have misconfigured your scripts and tasks or \
your package manager's Workspace structure."
)]
#[diagnostic(
code(recursive_turbo_invocations),
url(
"{}/messages/{}",
TURBO_SITE, self.code().unwrap().to_string().to_case(Case::Kebab)
)
)]
RecursiveTurbo {
task_name: String,
command: String,
#[label("task found here")]
#[label("This script calls `turbo`, which calls the script, which calls `turbo`...")]
span: Option<SourceSpan>,
#[source_code]
text: NamedSource,
Expand Down Expand Up @@ -215,6 +225,7 @@ impl<'a> Visitor<'a> {
Some(cmd) if info.package() == ROOT_PKG_NAME && turbo_regex().is_match(cmd) => {
package_task_event.track_error(TrackedErrors::RecursiveError);
let (span, text) = cmd.span_and_text("package.json");

return Err(Error::RecursiveTurbo {
task_name: info.to_string(),
command: cmd.to_string(),
Expand Down
40 changes: 40 additions & 0 deletions docs/repo-docs/messages/recursive-turbo-invocations.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: Recursive `turbo` invocations
description: Learn more about errors with recursive sccripts and tasks in Turborepo.
---

## Why this error occurred

When a cycle of `turbo` invocations is detected in a [single-package workspace](https://turbo.build/repo/docs/guides/single-package-workspaces), Turborepo will error to prevent the recursive calls to itself. Typically, this situation occurs for one of two reasons:

### Recursion in scripts and tasks

In a single-package workspace, a script in `package.json` that calls a Turborepo task with the same name causes a loop.

```json title="./package.json"
{
"scripts": {
"build": "turbo run build"
}
}
```

Calling the `build` script calls `turbo run build`. `turbo run build` then calls the `build` script, initiating the loop of recursive calls.

To resolve this, ensure that the name of the script in `package.json` is not the same as the Turborepo task. For example, to fix the snippet above, renaming the script would break the cycle:

```json title="./package.json"
{
"scripts": {
"build:app": "turbo run build"
}
}
```

### Package manager Workspace misconfiguration

A misconfigured workspace can make it appear that a [multi-package workspace](https://vercel.com/docs/vercel-platform/glossary#multi-package-workspace) is a single-package workspace. This causes Turborepo to infer that the repository is of the wrong type, causing it to see the script in `package.json` to be recursive.

Your repo can end up in this state in a few ways, with the most common being that the [packages are not defined according to your package manager](https://turbo.build/repo/docs/crafting-your-repository/structuring-a-repository#specifying-packages-in-a-monorepo). An npm workspace that is missing the `workspaces` field in `package.json` or a pnpm workspace that is missing a `pnpm-workspace.yaml` file can result in this error message.

Check that your repository is complying with standards for multi-package workspaces and correct any issues.
10 changes: 7 additions & 3 deletions turborepo-tests/integration/tests/recursive-turbo.t
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ sed replaces the square brackets with parentheses so prysk can parse the file pa
\xe2\x80\xa2 Packages in scope: //, another, my-app, util (esc)
\xe2\x80\xa2 Running something in 4 packages (esc)
\xe2\x80\xa2 Remote caching disabled (esc)
x root task //#something (turbo run build) looks like it invokes turbo and
| might cause a loop
recursive_turbo_invocations (https://turbo.build/messages/recursive-turbo-invocations)

x Your `package.json` script looks like it invokes a Root Task (//
| #something), creating a loop of `turbo` invocations. You likely have
| misconfigured your scripts and tasks or your package manager's Workspace
| structure.
,-\(.*package.json:3:1\) (re)
3 | "scripts": {
4 | "something": "turbo run build"
: ^^^^^^^^|^^^^^^^^
: `-- task found here
: `-- This script calls `turbo`, which calls the script, which calls `turbo`...
5 | },
`----

Expand Down
4 changes: 4 additions & 0 deletions turborepo-tests/integration/tests/run/missing-tasks.t
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ Setup
$ ${TURBO} run something --dry > OUTPUT 2>&1
[1]
$ grep --quiet -E "root task (//#)?something \(turbo run build\) looks like it invokes turbo and" OUTPUT
[1]
$ grep --quiet -E "might cause a loop" OUTPUT
[1]

# Bad command

$ ${TURBO} run something > OUTPUT2 2>&1
[1]
$ grep --quiet -E "root task (//#)?something \(turbo run build\) looks like it invokes turbo and" OUTPUT
[1]
$ grep --quiet -E "might cause a loop" OUTPUT
[1]

0 comments on commit 7a92d0b

Please sign in to comment.