---
title: "MGS1004: unreached footprint declaration"
description: Fires when a ctx.readsFiles or ctx.writesFiles call is not statically reachable from a target body, so the declared glob never enters a cache key.
tags: [MGS1004, magusfile, cache, reads-files, writes-files, doctor]
---

# MGS1004: unreached footprint declaration

`magus doctor` found a `ctx.readsFiles(...)` or `ctx.writesFiles(...)` call that the
static extractor cannot reach from any target body. magus reads these
declarations from the source without running it (a cache hit skips the body
entirely), following a target's body and the helpers it calls by name. A call it
can't reach never enters a cache key.

```text
[MGS1004] 1 ctx.readsFiles/writesFiles call(s) are not statically reachable from a
target body, so they never enter a cache key; call them directly in the target
body (see .../MGS1004.md)
  ctx.readsFiles in srcGlobs (web/magusfile.buzz:14)
```

## Why

A per-target footprint has to be known _before_ the target runs, because on a
cache hit the body never executes. So magus recovers `ctx.readsFiles`/`ctx.writesFiles`
statically: it walks each `export fun` target and the helper functions it calls
by a plain name (`srcGlobs()`), and collects the string-literal globs it finds.

A declaration outside that reach is invisible to the cache:

- a call in a helper that no target (transitively) calls by name - often dead
  code;
- a call reached only through indirection - the identifier used as a value
  (`final f = ctx.readsFiles; f("src/**")`) or dynamic dispatch, which the static
  read cannot follow.

The danger is silent under-declaration: the input you thought you declared is not
in the key, so editing it produces no miss and you replay a stale build. This
check makes that loud. It is the counterpart to the hard load error you get for a
non-literal argument in a _reached_ call (`ctx.readsFiles(someVar)`) - that one
magus can see and rejects immediately; an _unreached_ call it can only warn about.

This is a **warning**, not a load error: an unreached call may simply be dead
code, which is harmless.

## Resolution

Call `ctx.readsFiles`/`ctx.writesFiles` directly in the target body, or from a helper
the target invokes by name:

```buzz
// Before: the glob lives in a helper nothing calls, so it never keys anything.
fun srcGlobs() > void { ctx.readsFiles("src/**"); }
export fun build(ctx: magus\Context, args: [str]) > void { go["go-build"](ctx); }

// After: declared in the body (or a bare-called helper), so it enters the key.
export fun build(ctx: magus\Context, args: [str]) > void {
    ctx.readsFiles("src/**");
    go["go-build"](ctx);
}
```

If the flagged call is genuinely dead code, delete it.

## What this is NOT

- **Not a hard error.** Nothing blocks the build; it is a `magus doctor` finding.
- **Not the non-literal-argument error.** A computed argument in a _reachable_
  call (`ctx.readsFiles(x)`) is a magusfile load error, because magus sees the call
  but cannot resolve the glob. MGS1004 is the opposite: magus resolves nothing
  because it never reaches the call.

## See also

- [cache.md](../../../concepts/cache.md#per-target-inputs-and-outputs): how `ctx.readsFiles` and
  `ctx.writesFiles` declare a target's per-target footprint.
- [dependencies.md](../../../concepts/dependencies.md): the static-extraction discipline
  ctx.readsFiles shares with `magus\needs`.
