TurboRepo 1.12 output validation: tasks failing in CI after upgrade with no declared outputs
0 reputation · 14 Jul 2020, 05:32 UTC
0 reputation · 14 Jul 2020, 05:32 UTC
After moving a monorepo CI pipeline from TurboRepo 1.11 to 1.12, several tasks that previously ran fine now fail during turbo run. The affected tasks (lint, typecheck, and a deployment packaging step) never declared outputs in turbo.json because they were treated as side-effect-only or produced artifacts we did not cache.
The logs appear to contain a validation error stating that a task has no outputs declared, and the task is marked failed before it executes. This blocks the deployment job entirely, so I cannot tell whether the deployment itself is broken or whether the failure is purely the new validation gate.
Constraints: the pipeline must stay on 1.12.x for now, some tasks genuinely produce no files, and I would prefer not to declare fake output paths just to satisfy validation. I have seen mention of a --skip-output-validation flag in the 1.12.x line, but it does not appear in the main release notes, so its stability is unclear.
Is declaring outputs: [] the intended way to keep side-effect-only tasks valid under 1.12? Is --skip-output-validation safe to rely on, or is it expected to be removed? How should I distinguish validation failures from real deployment errors when reading the logs?
30775 reputation · 14 Jul 2020, 07:27 UTC
Yes — for tasks that genuinely produce nothing, declaring "outputs": [] explicitly is the intended, stable way to tell TurboRepo "this task is expected to emit no artifacts." It is not a fake path and it does not poison the cache; it simply removes the ambiguity between "you forgot to declare outputs" and "this task has none by design." That is the fix I would apply for lint, typecheck, and the packaging step, and I would avoid --skip-output-validation in a production pipeline.
Confirmed by your symptoms: the failure happens before the task executes, which means it occurs during task-graph resolution, not in your deployment script. Your deployment job is almost certainly fine — it is being blocked upstream by the validation gate, so you cannot yet conclude anything about the deployment itself.
Likely, but worth verifying: that 1.12 tightened validation so tasks with no outputs key at all are treated differently from tasks with an explicit empty array. Omitting outputs has historically meant "nothing to cache," and lint/typecheck tasks ran fine under that reading — which matches your 1.11 behavior. If 1.12 now rejects the omitted-key case while accepting "outputs": [], that fully explains what you are seeing. I could not verify the exact 1.12 changelog wording here, so treat the precise rule change as unconfirmed and validate it against your own logs (below).
In turbo.json, make the intent explicit for each affected task:
{
"tasks": {
"lint": { "outputs": [] },
"typecheck": { "outputs": [] },
"deploy#package": { "outputs": ["dist/**"] }
}
}Note the distinction: lint and typecheck are true no-output tasks, but a "deployment packaging step" that produces an artifact should declare its real output directory — otherwise Turbo cannot cache it and, more importantly, downstream tasks that depend on the artifact may not re-run correctly. Only use [] where nothing is written.
A flag that does not appear in the release notes should be treated as experimental or internal. Even if it works on your exact 1.12.x patch, it can change behavior or disappear in any minor bump — precisely the upgrade fragility that caused this incident. Use it, at most, as a temporary unblock while you roll out explicit outputs declarations, not as the fix.
turbo run lint --force locally on 1.12. If it fails identically with no task output, it is validation. Then add "outputs": [] and re-run — if the task now executes, the gate was the whole problem.turbo run deploy --dry-run shows whether graph resolution succeeds independent of execution; a validation failure will surface there too.Once outputs: [] is in place, any remaining failure in the deployment job will be a genuine task failure, and the logs will contain your script's own error output — that is how you separate the two classes cleanly.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.