BigPizzaV3/CodexPlusPlus · error

Invalid runtime key

Error message

Invalid runtime key

What it means

prepare() validates the runtime key string before joining it into paths (runtime_root/<key>/...). The key must pass key_valid() (a restricted identifier format); an invalid key could otherwise be used for path traversal or point at a non-existent runtime directory, so preparation fails fast.

Solutions

  1. Use only runtime keys produced by discover()/the plugin itself (version-identifier style, no slashes or '..')
  2. Fix or delete the descriptor whose command path yields the invalid key
  3. Clear the corrupted state and reinstall the plugin so a valid key is generated

Example fix

// before
prepare(&paths, "../../etc", &contract)?;
// after
prepare(&paths, "2026-09-01-abc123", &contract)?;
Defensive patterns

Strategy: validation

Validate before calling

fn key_valid(key: &str) -> bool { !key.is_empty() && key.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') }
if !key_valid(key) { eprintln!("{key:?} is not a valid runtime key"); }

Type guard

fn is_valid_runtime_key(key: &str) -> bool {
    !key.is_empty() && key != "." && key != ".." && !key.contains(['/', '\\']) && key.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_' | '.'))
}

Prevention

When it happens

Trigger: prepare() is invoked by reconcile_locked with a key that fails key_valid() — e.g. a key containing path separators, '..', empty string, or characters outside the allowed identifier set, typically originating from a malformed descriptor or corrupted state.

Common situations: Hand-edited .mcp.json command paths yielding odd keys; corrupted state files carrying a bad key; keys copied from another platform with different naming rules.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/a784851b8981dd5a. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/native_browser.rs:368

        }
        count += 1;
        ensure!(count <= 64, "Too many plugin descriptors");
        let descriptor = entry.path().join(".mcp.json");
        if !descriptor.exists() {
            continue;
        }
        let data: Value = serde_json::from_slice(&read_regular(&descriptor, 1024 * 1024)?)?;
        keys.insert(selected_key(&data, &paths.runtime_root)?);
    }
    ensure!(
        keys.len() <= 1,
        "Ambiguous runtime selection; no cache was modified"
    );
    Ok(keys.into_iter().next())
}

fn prepare(paths: &BrowserPaths, key: &str, contract: &RuntimeContract) -> Result<()> {
    ensure!(key_valid(key), "Invalid runtime key");
    let runtime = paths.runtime_root.join(key);
    let target = runtime.join(SERVICE);
    let _runtime_guards = pin_parents(&target)?;
    for (file, expected) in &contract.files {
        ensure!(
            sha(&read_regular(&runtime.join(file), 128 * 1024 * 1024)?) == *expected,
            "Unsupported native runtime component: {file}"
        );
    }
    let mut current = read_regular(&target, MAX_SERVICE)?;
    let backup_dir = paths.state_root.join(key);
    plain_path(&backup_dir)?;
    fs::create_dir_all(&backup_dir)?;
    let _backup_guards = pin_parents(&backup_dir.join("journal.json"))?;
    let backup = backup_dir.join("original.mjs");
    let journal_path = backup_dir.join("journal.json");
    let control = paths.state_root.join("control.json");
    if journal_path.exists() {

View on GitHub (pinned to b1ed92e5e4)