---
title: "MGS2013: a sandboxed read of a set variable answered unset"
description: Fires when code under the sandbox reads an environment variable that is set but that the sandbox policy withholds. The read still answers unset, so the code gets nothing it was not granted; this names the variable and the target so whoever reads the run knows why.
tags: [MGS2013, sandbox, environment, passthrough, os.env, env.lookup, env.expand, secrets]
---

# MGS2013: a sandboxed read of a set variable answered unset

Code running under the sandbox read an environment variable that is set in magus's own
environment, and the sandbox withholds it, so the read answered "unset":

```text
[warn] [MGS2013] a sandboxed read of a set variable answered unset name=ACTIONS_RUNTIME_TOKEN project=. target=test
```

## Why

`os\env`, `env\get`, `env\lookup` and `env\expand` read through the sandbox's environment
allowlist, and a name the allowlist withholds reads exactly like a name nobody set. The
code gets nothing it was not granted.

It also makes the failure invisible. The variable is right there in your shell, the code
that reads it sees nothing, and whatever it does next (skip a step, report a cache miss,
fall back to a default) looks like ordinary behavior. This line says what happened. It
goes to the run's log, never back to the code that read, and names the variable, never its
value. It fires once per target and variable. A listing (`env\list`) reports nothing: it
would name every withheld variable at once.

The line does tell whoever reads the log that a variable of that name is set. Names are
not secrets, but a workflow that keeps one private should know the log carries it.

[MGS2003](MGS2003.md) is the other half: the variables a child process did not inherit.

## Resolution

If the code needs the variable and it is not a credential, declare it in the narrowest
layer that needs it: the target's own `sandbox` policy, the spell that drives the tool, or,
for every target, the workspace:

```yaml
sandbox:
  env:
    passthrough:
      - GOFLAGS
```

If it is a credential, do not pass it through to every target. Read it with
`magus\secret.read`, which keeps it out of logs, and hand it to the one command that needs
it.

A remote cache backend reads its own credentials under its own declaration, never a
target's: its lookups and stores run under the workspace policy plus that spell's layer.
A cache backend whose read still hits this has not declared the variable in its
`mgs_getSandbox`.

## See also

- [MGS2003](MGS2003.md): variables a child process did not inherit
- [Sandbox model](../../../concepts/sandbox.md#which-spells-a-process-gets): which spell
  layers a process gets, and why a service spell's reach only its own ops
- [Secrets](../../../concepts/secrets.md): reading a credential so magus can protect it
