Hmbown/CodeWhale · error · anyhow::Error
refusing non-loopback app-server bind without explicit auth
Error message
refusing non-loopback app-server bind without explicit auth token; pass --auth-token or set CODEWHALE_APP_SERVER_TOKEN
What it means
open_bundle_directory opens a plugin bundle directory on Windows with BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT and deny-write sharing, then requires the handle to be a real directory without the reparse attribute. A junction or directory-symlink, or a path that actually names a file, is rejected with InvalidData.
Source
Thrown at crates/app-server/src/lib.rs:701
fn resolve_auth_token(options: &AppServerOptions) -> Result<Option<String>> {
let configured = options.auth_token.as_ref().map(|token| token.trim());
if let Some(token) = configured
&& token.is_empty()
{
bail!("app-server auth token cannot be empty");
}
let has_explicit_token = configured.is_some();
if options.insecure_no_auth {
if !options.listen.ip().is_loopback() {
bail!("refusing unauthenticated app-server bind on non-loopback address");
}
eprintln!("warning: app-server HTTP auth disabled by --insecure-no-auth");
return Ok(None);
}
if !has_explicit_token && !options.listen.ip().is_loopback() {
bail!(
"refusing non-loopback app-server bind without explicit auth token; pass --auth-token or set CODEWHALE_APP_SERVER_TOKEN"
);
}
let token = configured
.map(str::to_string)
.unwrap_or_else(|| format!("cwapp_{}", Uuid::new_v4().simple()));
for line in app_server_auth_status_lines(has_explicit_token) {
eprintln!("{line}");
}
Ok(Some(token))
}
fn app_server_auth_status_lines(has_explicit_token: bool) -> Vec<&'static str> {
if has_explicit_token {
return vec!["app-server auth: bearer token required for HTTP routes."];
}
vec![View on GitHub (pinned to 0c42157ee5)
Solutions
- Copy the plugin tree to a real directory instead of junctioning it, and point Codewhale there
- Reinstall the plugin so Codewhale stages its own directory
- Remove the junction ('rmdir <link>' removes only the link on Windows) and restore a physical directory
Defensive patterns
Strategy: validation
Validate before calling
const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x400;
fn is_real_directory(path: &std::path::Path) -> bool {
match std::fs::symlink_metadata(path) {
Ok(md) => md.is_dir() && md.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT == 0,
Err(_) => false,
}
} Type guard
fn is_physical_dir(p: &std::path::Path) -> bool {
std::fs::symlink_metadata(p).map(|m| m.is_dir() && m.file_attributes() & 0x400 == 0).unwrap_or(false)
} Try / catch
match open_bundle_directory(&path) {
Err(e) if e.to_string().contains("reparse point or non-directory") => {
// replace the junction with a copied, real directory and retry
}
other => other?,
} Prevention
- Copy plugin trees instead of junctioning them between installs
- Check 'fsutil reparsepoint query <dir>' when a plugins dir was restored from backup
- Keep one plugins directory per installation
When it happens
Trigger: Reviewing a plugin whose directory is a junction or directory symlink (for example a shared plugins dir linked into multiple installs), or passing a file path where a bundle directory is expected.
Common situations: Portable installs sharing one plugins directory via junction; backup restores that materialize directories as links; dedupe tools junctioning large folders.
Related errors
- refusing unauthenticated app-server bind on non-loopback add
- timed out waiting for runtime API bridge at {}/health
- Codewhale-owned xAI OAuth path must not be a reparse point
- {} is a reparse point, not a workspace-owned file
- app-server auth token cannot be empty
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/d93e9ececf7513b6.
Report an issue: GitHub.