---
title: "MGS1025: magusfile calls an API that was removed"
description: Fires when a magusfile calls a magus function that no longer exists, such as magus.needs or magus.glob. Buzz reads a missing member as null, so these used to fail only at run time and to drop dependency edges from the target graph.
tags: [MGS1025, magusfile, migration, needs, target-graph, compatibility]
---

# MGS1025: magusfile calls an API that was removed

A magusfile called a function magus no longer binds:

```text
[MGS1025] magus.needs was removed: call ctx.needs(<target>) instead
```

magus rejects this at load, before any target runs.

## Why this needs its own code

Removing a binding does not report itself. Buzz reads a missing member as `null`
rather than erroring, so a magusfile calling a removed function still parses, still
loads, and still passes `magus ls`. It breaks when the target finally runs, and only
as:

```text
cause: buzz: null is not callable
```

That names neither the call nor what replaced it. Nothing in it says the API changed.

`magus.needs` was worse than a bad message. magus builds the target dependency graph
statically, by reading `ctx.needs` out of the source, so a magusfile still calling
`magus.needs` declares its dependencies to nothing:

```console
$ magus explain build
target:.:build   target
part of  project:.
```

No `depends on` line. The edge is simply absent, and `magus explain`, `magus
affected`, and scheduling all act on that graph. A stale magusfile could run for a
long time building in the wrong order, or skipping a prerequisite entirely, without
ever producing an error.

## The removed calls

| Removed                                  | Use instead                                              |
| ---------------------------------------- | -------------------------------------------------------- |
| `magus.needs(<target>)`                  | `ctx.needs(<target>)`                                    |
| `magus.glob("<pattern>")`                | `ctx.glob("<pattern>")`                                  |
| `magus.target.literal("<name>")`         | pass the target function itself to `ctx.needs`           |
| `magus.project.register(fun(p, cb) ...)` | `magus\project({...})` at the top level                  |
| `magus.insightMarkdown([opts])`          | build the document from `magus\insight()`'s typed report |

The first three moved onto the context that every target now receives, which is what
makes the dependency graph readable from the source. See
[MGS1008](MGS1008.md) for the signature half of that same change.

## Resolution

Take the declarations off the `magus` module and onto the target's `ctx`:

```buzz
// before
export fun build(ctx: magus\Context, args: [str]) > void {
    magus.needs(magus.target.literal("format"));
    magus.needs(magus.glob("gen-*"));
}

// after
export fun build(ctx: magus\Context, args: [str]) > void {
    ctx.needs(format);
    ctx.needs(ctx.glob("gen-*"));
}
```

`ctx.needs` takes the target function itself, not its name as a string, so a typo is
a load error rather than a dependency that silently never resolves.

Registration moved from a callback to a literal, and is called at the top level of
the magusfile rather than from inside a function:

```buzz
// before
magus.project.register(fun(p, cb) > bool { cb({"spells": [go]}); return true; });

// after
magus\project({ "spells": [go] });
```

## Detection

magus reads the parsed magusfile, so a removed name inside a comment or a string
literal is not a call and does not fire this code. The one exception is a magusfile
that fails to parse at all: `magus.project.register` predates required parameter
annotations, so it dies in the parser before there is an AST to read, and magus falls
back to a textual scan to explain it. That fallback runs only for a magusfile that is
already failing to load.

## See also

- [MGS1008](MGS1008.md): the target signature half of the context migration
- [Targets](../../../concepts/targets.md)
