---
title: "MGS1012: cross-project output forms a dependency cycle"
description: Fires at load when one target both reads from and writes into the same project, because a cross-project input and a cross-project output imply opposite build-order edges.
tags: [MGS1012, magusfile, outputs, inputs, cross-project, cycle, ordering]
---

# MGS1012: cross-project output forms a dependency cycle

A target declared a cross-project input on a project **and** a cross-project
output into that same project. The two imply opposite build orders, so there is
no order that satisfies both.

```text
[MGS1012] renderer: ctx.writesFiles writes "out.html" into "site", so site must run
after renderer, but renderer already depends on site; a target cannot both read
from and write into the same project
```

## Why

The two cross-project declarations are not symmetric, and that is the whole point
of having both:

| Declaration | Meaning | Edge |
| --- | --- | --- |
| `ctx.readsFiles(site.file("src.md"))` | I read a file `site` owns | renderer depends on site |
| `ctx.writesFiles(site.file("out.html"))` | I produce a file `site` owns | site depends on renderer |

An input says *build me after them, so I read finished bytes*. An output says
*build them after me, so they see finished bytes*. Declare both against one
project and each is waiting on the other.

This is not an exotic mistake. It is the most natural thing to reach for: read a
project's sources, render them, write the result back next to them. The shape is
reasonable; it is the project boundary that is drawn in the wrong place.

## Why it fails here and not later

The dependency graph would catch the cycle eventually, but far too late to be
useful:

- The error named neither project nor file - just `graph: dependency cycle`.
- It took down `magus graph` and the entire affected pipeline for **every**
  project in the workspace, not the two involved.
- It was scope-dependent. A run selecting only the writer never builds the full
  graph, so `magus run build renderer` reported success on a workspace that
  `magus graph deps` refused to load at all.

Failing at load makes it deterministic and attributable: the same workspace fails
the same way for every command, and the message names both halves.

## Resolution

Pick whichever is true of your target.

**It is a consumer.** If it only reads `site` and the output was a mistake, drop
the cross-project output and write into its own tree:

```buzz
ctx.readsFiles(site.file("src.md"));
ctx.writesFiles("dist/out.html");
```

**It is a producer.** If it genuinely generates part of `site`, drop the
cross-project input. A writer does not usually need to declare an input on the
project it writes into - and if it reads a *different* project's sources, declare
the input there instead.

**Both are true.** Then the read and the write are two different concerns sharing
one project by accident. Split the generated file into a third project that
neither reads from, and have `site` declare a cross-project input on it. That is
the shape the ordering model is built for, and it stays acyclic.

## See also

- [MGS1011](MGS1011.md) - a cross-project output naming a project magus cannot use
- [Cross-project dependencies](../../../concepts/dependencies.md)
