---
title: "MGS1029: source glob reaches into a pruned directory"
description: Fires when a declared source glob's static prefix lands inside a directory the expansion walk skips, so the pattern matches nothing and contributes no cache key.
tags: [MGS1029, magusfile, sources, cache, globs]
---

# MGS1029: source glob reaches into a pruned directory

A target declares a source pattern whose directory prefix passes through a tree
magus prunes, so it matches nothing and keys nothing.

```text
[MGS1029] docs: source glob "proto/gen/*.binpb" can never match: the expansion
walk prunes "gen"
```

## Why

The walk that builds a cache key skips four directory names wholesale, from
`project.IgnoreDirs`:

| directory      | why it is pruned                                              |
| -------------- | ------------------------------------------------------------- |
| `gen`          | generated code: machine-written, never a discoverable project |
| `vendor`       | Go vendored dependencies                                      |
| `node_modules` | pnpm/npm dependencies                                         |
| `target`       | Rust build artifacts                                          |

Pruning them is what keeps a broad pattern honest. A project declaring `**/*.js`
means its own sources; without the prune it would hash every file in
`node_modules` and key on a dependency tree nobody edits.

The cost is that a pattern _aimed at_ one of those directories can never match.
It is not an error at authoring time and not an error at run time - the glob is
well-formed, it simply describes files the walk will not visit. The target then
replays from cache while the files it named change underneath it.

This is the input-side twin of [MGS1014](MGS1014.md), which catches a declared
**output** that no run produces. Same shape: a declaration and reality quietly
disconnected.

## What it is not

An **exact** path is fine, and is not reported:

```buzz
ctx.readsFiles(workspace.file("proto/gen/descriptor.binpb"));   // works
ctx.readsFiles(workspace.file("proto/gen/*.binpb"));            // MGS1029
```

A wildcard-free declaration names one file, so magus resolves it by stat rather
than by the walk. It reaches the cache key normally even from inside a pruned
tree. Only a pattern is unmatchable, because only a pattern could expand to
swallow a dependency tree.

## Fix

Name the file exactly, if you meant one file:

```buzz
ctx.readsFiles(workspace.file("proto/gen/descriptor.binpb"));
```

Or declare the real sources instead of the generated artifact. A generated tree
is downstream of something you own, and keying on that something is both
narrower and more honest:

```buzz
// not proto/gen/*.binpb - the schema the descriptor is built from
ctx.readsFiles(workspace.file("proto/magus/**/*.proto"));
```

If the files genuinely live under a pruned name and are genuinely sources,
rename the directory. `gen`, `vendor`, `node_modules` and `target` carry a
meaning across every magus workspace, and a hand-written source tree under one
of those names will surprise more than this check.

## Confirming the fix

Ask what the cache key actually contains:

```sh
magus describe target <target> <project> --cache --inputs
```

Every hashed file appears as a `src:` line. If a file you declared is missing
from that list, it is not in the key, whatever `describe target` reports under
`sources:`.
