unicity-aos/aos-ce · warning · Finding

[env]. sets scope = "shared", which is silently ignored.

Error message

[env].{key} sets scope = "shared", which is silently ignored.

What it means

check_env inspects each [env] entry in the manifest and warns when an entry sets scope = "shared". Manifest-declared env scope is silently ignored by the host — the field has no effect — so forge warns authors to remove it rather than rely on a no-op setting.

Solutions

  1. Remove the `scope` key from the [env] entry; it is never honored from the manifest.
  2. If sharing is required, configure shared/operator env scope through the host/operator configuration, not the manifest.
  3. Keep only plain value entries under [env] (e.g. `[env.MY_VAR]\nvalue = "x"`).

Example fix

# before
[env.API_URL]
value = "https://api.example.com"
scope = "shared"
# after
[env.API_URL]
value = "https://api.example.com"
Defensive patterns

Strategy: validation

Validate before calling

# fail CI if any env entry carries a scope key
grep -n 'scope' capsule.toml && echo "manifest env scope is ignored; remove it" && exit 1

Prevention

When it happens

Trigger: validate_manifest -> check_env finds any env table entry whose `scope` sub-value is the string "shared"; e.g. `[env.MY_VAR]\nscope = "shared"`.

Common situations: Authors assuming manifest env can be shared with the operator or other capsules; copying an env block from an old manifest format where scope was honored; cargo-culting config from a different capsule system.

Related errors


AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13). Data as JSON: /api/errors/7dbad1ca0e783408. Report an issue: GitHub.

Appendix: source

Thrown at capsules/capsule-forge/src/checks.rs:184

                .and_then(Toml::as_str)
                .is_some_and(|f| !f.is_empty())
        })
    });
    if !has_file {
        out.push(Finding::err(
            "No [[component]] with a `file` was found.",
            "Add a [[component]] table with `id`, `file = \"my_capsule.wasm\"`, `type = \"executable\"`.",
        ));
    }
}

fn check_env(root: &Toml, out: &mut Vec<Finding>) {
    let Some(env) = root.get("env").and_then(Toml::as_table) else {
        return;
    };
    for (key, val) in env {
        if val.get("scope").and_then(Toml::as_str) == Some("shared") {
            out.push(Finding::warn(
                format!("[env].{key} sets scope = \"shared\", which is silently ignored."),
                "Remove `scope`; shared/operator-only env scope is not honoured from the manifest.",
            ));
        }
    }
}

/// Collect the `[publish]` and `[subscribe]` topic keys, warning if a table is
/// empty (fail-closed: the capsule then can't publish/subscribe at all).
fn collect_topics(root: &Toml, out: &mut Vec<Finding>) -> (Vec<String>, Vec<String>) {
    let pub_keys = table_keys(root, "publish");
    let sub_keys = table_keys(root, "subscribe");
    if pub_keys.is_empty() {
        out.push(Finding::warn(
            "[publish] is empty or missing — the capsule cannot publish any event.",
            "Add the tool-bus publish keys `tool.v1.execute.*.result` and `tool.v1.response.describe.*`.",
        ));
    }

View on GitHub (pinned to f6f22024fb)