BigPizzaV3/CodexPlusPlus · warning

throw new Error( );

Error message

throw new Error({});

What it means

In codex-plus-core's build_enabled_bundle, each enabled user script file is read with fs::read_to_string. If reading a script file fails (missing file, permission denied, invalid UTF-8, IO error), the reader does not abort the bundle build; instead it substitutes a JavaScript stub `throw new Error("...");` as the script body, embedding the Rust IO error message. This means the generated bundle will raise this error in the JS runtime whenever that script executes, surfacing the file-read failure at script-run time rather than bundle-build time.

Solutions

  1. Locate the failing script from the embedded error message and either recreate the file at its configured path or disable/remove it from the script list.
  2. Re-save the script file as UTF-8 (read_to_string fails on invalid UTF-8).
  3. Fix file permissions on the script file so the process user can read it.
  4. If you want build-time detection instead of runtime throws, change the code to fail the scan (propagate the io::Error with `?`) or log a warning and skip disabled/broken scripts.

Example fix

// before: silent stub that throws at script runtime
let source = fs::read_to_string(&script.path)
    .unwrap_or_else(|error| format!("throw new Error({});", json!(error.to_string())));
// after: propagate the failure at bundle-build time
let source = fs::read_to_string(&script.path)
    .map_err(|error| anyhow!("failed to read script {:?}: {}", script.path, error))?;
Defensive patterns

Strategy: validation

Validate before calling

for script in &scripts {
    if script.enabled && !script.path.exists() {
        eprintln!("warning: enabled script missing: {:?}", script.path);
    }
    if script.enabled && !script.path.is_file() {
        // exclude from bundle or fail fast
    }
}

Type guard

fn readable_utf8_script(path: &Path) -> bool {
    path.is_file() && fs::read(path).map(|bytes| std::str::from_utf8(&bytes).is_ok()).unwrap_or(false)
}

Try / catch

// Rust has no try/catch; wrap the bundle build and surface the substituted runtime error
match build_enabled_bundle(&config) {
    Ok(bundle) => install(bundle),
    Err(e) => eprintln!("脚本包构建失败: {e}"),
}
// at runtime, catch the injected throw
try { runUserScript(source); } catch (e) { console.error("脚本文件读取失败:", e.message); }

Prevention

When it happens

Trigger: Calling build_enabled_bundle (via the apply/scan script pipeline) while an enabled script's path no longer exists, is unreadable due to permissions, or contains invalid UTF-8 bytes.

Common situations: User deleted or renamed a script file but left it marked enabled in the scripts config; scripts directory moved or synced incompletely between machines; a symlink pointing to a removed target; a non-UTF8 (e.g. UTF-16 or GBK) script file saved by a Windows editor.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at crates/codex-plus-core/src/user_scripts.rs:246

            "enabled": config.enabled,
            "builtin_dir": self.builtin_dir.to_string_lossy(),
            "user_dir": self.user_dir.to_string_lossy(),
            "scripts": scripts
        }))
    }

    pub fn build_enabled_bundle(&self) -> anyhow::Result<String> {
        let config = self.load_config();
        if !config.enabled {
            return Ok(String::new());
        }
        let mut blocks = Vec::new();
        for script in self.scan_script_files(&config)? {
            if !script.enabled {
                continue;
            }
            let source = fs::read_to_string(&script.path)
                .unwrap_or_else(|error| format!("throw new Error({});", json!(error.to_string())));
            blocks.push(wrap_script(&script, &source));
        }
        Ok(blocks.join("\n"))
    }

    fn scan_scripts(
        &self,
        config: &UserScriptConfig,
        runtime_status: Option<&Value>,
    ) -> anyhow::Result<Vec<Value>> {
        let runtime_scripts = runtime_status
            .and_then(|value| value.get("scripts").or(Some(value)))
            .and_then(Value::as_object);
        Ok(self
            .scan_script_files(config)?
            .into_iter()
            .map(|script| {
                let market = config.market.get(&script.key);

View on GitHub (pinned to b1ed92e5e4)