#![allow(clippy::unwrap_used, clippy::expect_used)]
#![cfg(feature = "subprocess")]
use std::collections::HashMap;
use std::time::Duration;
use kaish_kernel::ast::Value;
use kaish_kernel::{Kernel, KernelConfig};
fn repl_kernel() -> Kernel {
let mut vars = HashMap::new();
vars.insert(
"PATH".to_string(),
Value::String(std::env::var("PATH").unwrap_or_default()),
);
let config = KernelConfig::repl().with_initial_vars(vars);
Kernel::new(config).expect("Failed to create kernel")
}
#[tokio::test]
async fn external_command_basic() {
let kernel = repl_kernel();
let result = kernel.execute("true").await.unwrap();
assert!(result.ok(), "true should succeed: {:?}", result);
}
#[tokio::test]
async fn external_command_with_args() {
let kernel = repl_kernel();
let result = kernel.execute("echo hello world").await.unwrap();
assert!(result.ok());
}
#[tokio::test]
async fn large_buffered_stdin_does_not_deadlock() {
let kernel = repl_kernel();
let payload = "x".repeat(8 * 1024 * 1024);
let fut = kernel.execute_with_options(
"bash -c \"cat\"",
kaish_kernel::ExecuteOptions::new().with_stdin(payload.clone()),
);
let result = tokio::time::timeout(Duration::from_secs(10), fut)
.await
.expect("large buffered stdin must not deadlock")
.expect("execute");
assert_eq!(result.code, 0, "cat should succeed: {}", result.err);
assert_eq!(
result.text_out().len(),
payload.len(),
"all stdin bytes should round-trip through cat"
);
}
#[tokio::test]
async fn external_resolution_is_hermetic_no_os_path_fallback() {
let kernel = Kernel::new(KernelConfig::repl()).expect("kernel"); let result = kernel.execute("printenv").await.unwrap();
assert_eq!(
result.code, 127,
"with no PATH in scope, external resolution must report command-not-found, \
not fall back to the OS PATH: {result:?}"
);
}
#[tokio::test]
async fn exporting_a_structured_value_to_a_subprocess_is_a_loud_error() {
let kernel = repl_kernel();
let result = kernel
.execute(r#"export CFG=$(fromjson '{"port":8080}'); printenv CFG"#)
.await;
let msg = match result {
Ok(r) => {
assert_ne!(r.code, 0, "exporting a record to a subprocess must fail: {r:?}");
r.err
}
Err(e) => format!("{e:#}"),
};
assert!(msg.contains("tojson"), "should hint at serializing with tojson: {msg}");
assert!(msg.contains("CFG"), "should name the offending variable: {msg}");
}
#[tokio::test]
async fn bare_collection_in_external_argv_is_a_loud_error() {
let kernel = repl_kernel();
let result = kernel
.execute(r#"xs=$(fromjson '[1,2]'); printenv $xs"#)
.await;
let msg = match result {
Ok(r) => {
assert_ne!(r.code, 0, "a bare collection argv element must fail: {r:?}");
r.err
}
Err(e) => format!("{e:#}"),
};
assert!(msg.contains("tojson"), "should hint at serializing with tojson: {msg}");
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn interpolated_collection_is_a_string_arg() {
let kernel = repl_kernel();
let result = kernel
.execute(r#"xs=$(fromjson '[1,2]'); /usr/bin/printf "[%s]" "$xs""#)
.await
.unwrap();
assert!(result.ok(), "interpolated collection arg must not error: {:?}", result);
assert_eq!(result.text_out(), "[[1,2]]");
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn spawn_nested_collection_argv_element_is_a_loud_error() {
let kernel = repl_kernel();
let result = kernel
.execute(r#"xs=$(fromjson '["ok",[1,2]]'); spawn --command /bin/echo --argv $xs"#)
.await;
let msg = match result {
Ok(r) => {
assert_ne!(r.code, 0, "a nested collection argv element must fail: {r:?}");
r.err
}
Err(e) => format!("{e:#}"),
};
assert!(msg.contains("tojson"), "should hint at serializing with tojson: {msg}");
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn spawn_record_as_whole_argv_is_a_loud_error() {
let kernel = repl_kernel();
let result = kernel
.execute(r#"r=$(fromjson '{"a":1}'); spawn --command /bin/echo --argv $r"#)
.await;
let ok = match result {
Ok(r) => r.code != 0,
Err(_) => true,
};
assert!(ok, "a record as the whole argv must be a loud error, not a silent empty argv");
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn exec_bare_collection_argv_is_a_loud_error() {
let kernel = repl_kernel();
let result = kernel
.execute(r#"xs=$(fromjson '[1,2]'); exec /bin/echo $xs"#)
.await;
let msg = match result {
Ok(r) => {
assert_ne!(r.code, 0, "a bare collection exec argv element must fail: {r:?}");
r.err
}
Err(e) => format!("{e:#}"),
};
assert!(msg.contains("tojson"), "should hint at serializing with tojson: {msg}");
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn env_bare_collection_argv_is_a_loud_error() {
let kernel = repl_kernel();
let result = kernel
.execute(r#"xs=$(fromjson '[1,2]'); env /bin/echo $xs"#)
.await;
let msg = match result {
Ok(r) => {
assert_ne!(r.code, 0, "a bare collection env argv element must fail: {r:?}");
r.err
}
Err(e) => format!("{e:#}"),
};
assert!(msg.contains("tojson"), "should hint at serializing with tojson: {msg}");
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn env_command_exporting_a_collection_var_is_a_loud_error() {
let kernel = repl_kernel();
let result = kernel
.execute(r#"export CFG=$(fromjson '{"port":8080}'); env /bin/true"#)
.await;
let msg = match result {
Ok(r) => {
assert_ne!(r.code, 0, "exporting a record through `env cmd` must fail: {r:?}");
r.err
}
Err(e) => format!("{e:#}"),
};
assert!(msg.contains("tojson"), "should hint at serializing with tojson: {msg}");
assert!(msg.contains("CFG"), "should name the offending variable: {msg}");
}
#[tokio::test]
async fn external_command_not_found() {
let kernel = repl_kernel();
let result = kernel
.execute("definitely_not_a_real_command_12345")
.await
.unwrap();
assert_eq!(result.code, 127, "Should return 127 for command not found");
assert!(
result.err.contains("command not found"),
"Error should mention 'command not found': {}",
result.err
);
}
#[tokio::test]
async fn external_command_not_found_under_errexit_keeps_message() {
let kernel = repl_kernel();
let result = kernel
.execute("set -e; definitely_not_a_real_command_12345")
.await
.unwrap();
assert_eq!(result.code, 127, "set -e must not change the exit code");
assert!(
result.err.contains("command not found"),
"set -e must not discard the external command's error message: {}",
result.err
);
}
#[tokio::test]
async fn external_command_date_format() {
let kernel = repl_kernel();
let result = kernel.execute("date +%s").await.unwrap();
assert!(result.ok(), "date +%s should succeed: {:?}", result);
let text = result.text_out();
let out = text.trim();
assert!(
out.chars().all(|c| c.is_ascii_digit()),
"date +%s should output digits: '{}'",
out
);
}
#[tokio::test]
async fn external_command_date_complex_format() {
let kernel = repl_kernel();
let result = kernel.execute("date +%Y-%m-%d").await.unwrap();
assert!(result.ok(), "date +%Y-%m-%d should succeed: {:?}", result);
let text = result.text_out();
let out = text.trim();
assert_eq!(out.len(), 10, "Date should be 10 chars: '{}'", out);
assert!(out.contains('-'), "Date should have dashes: '{}'", out);
}
#[tokio::test]
async fn external_command_short_flags() {
let kernel = repl_kernel();
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().display();
let result = kernel.execute(&format!("ls -la {path}")).await.unwrap();
assert!(result.ok(), "ls -la should succeed: {:?}", result);
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn external_command_long_flags() {
let kernel = repl_kernel();
let result = kernel.execute("spawn --command uname --argv '--kernel-name'").await.unwrap();
assert!(result.ok(), "spawn uname --kernel-name should succeed: {:?}", result);
assert!(
result.text_out().contains("Linux"),
"Should show Linux: {}",
result.text_out()
);
}
#[tokio::test]
async fn external_command_exit_code_success() {
let kernel = repl_kernel();
let result = kernel.execute("true").await.unwrap();
assert_eq!(result.code, 0, "true should exit with 0");
}
#[tokio::test]
async fn external_command_exit_code_failure() {
let kernel = repl_kernel();
let result = kernel.execute("false").await.unwrap();
assert_eq!(result.code, 1, "false should exit with 1");
}
#[tokio::test]
async fn external_command_exit_code_specific() {
let kernel = repl_kernel();
let result = kernel.execute("sh -c 'exit 42'").await.unwrap();
assert_eq!(result.code, 42, "Should preserve exit code 42");
}
#[tokio::test]
async fn external_command_stdin_piping() {
let kernel = repl_kernel();
let result = kernel.execute("echo 'hello world' | wc -c").await.unwrap();
assert!(result.ok(), "pipe should succeed: {:?}", result);
let count: i64 = result.text_out().trim().parse().unwrap_or(-1);
assert_eq!(count, 12, "wc -c should count 12 chars: {}", result.text_out());
}
#[tokio::test]
async fn external_command_respects_cwd() {
let kernel = repl_kernel();
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().to_string_lossy().to_string();
kernel.execute(&format!("cd {path}")).await.unwrap();
let result = kernel.execute("pwd").await.unwrap();
assert!(result.ok(), "pwd should succeed: {:?}", result);
assert!(
result.text_out().contains(&path),
"Should be in {}: {}",
path,
result.text_out()
);
}
#[tokio::test]
async fn pipeline_builtin_to_external() {
let kernel = repl_kernel();
let result = kernel
.execute("echo 'c\nb\na' | sort")
.await
.unwrap();
assert!(result.ok(), "pipeline should succeed: {:?}", result);
let text = result.text_out();
let lines: Vec<&str> = text.trim().lines().collect();
assert_eq!(lines, vec!["a", "b", "c"], "Should be sorted: {:?}", lines);
}
#[tokio::test]
async fn pipeline_builtin_to_builtin() {
let kernel = repl_kernel();
let result = kernel.execute("seq 1 10 | head -n 3").await.unwrap();
assert!(result.ok(), "pipeline should succeed: {:?}", result);
let text = result.text_out();
let lines: Vec<&str> = text.trim().lines().collect();
assert_eq!(lines, vec!["1", "2", "3"], "Should have first 3: {:?}", lines);
}
#[tokio::test]
async fn external_command_is_hermetic_by_default() {
let kernel = Kernel::new(KernelConfig::repl()).expect("kernel"); assert!(
std::env::var_os("PATH").is_some(),
"test precondition: PATH must be set for cargo test"
);
let result = kernel.execute("printenv PATH").await.unwrap();
assert!(
!result.ok(),
"printenv PATH must fail in hermetic kernel: {:?}",
result
);
}
#[tokio::test]
async fn external_command_sees_initial_vars() {
use kaish_kernel::ast::Value;
use std::collections::HashMap;
let mut vars = HashMap::new();
vars.insert("PATH".to_string(), Value::String("/usr/bin:/bin".into()));
vars.insert("MY_PROBE".to_string(), Value::String("seeded".into()));
let kernel = Kernel::new(KernelConfig::repl().with_initial_vars(vars))
.expect("Failed to create kernel");
let result = kernel.execute("printenv MY_PROBE").await.unwrap();
assert!(result.ok(), "printenv MY_PROBE should succeed: {:?}", result);
assert_eq!(result.text_out().trim(), "seeded");
}
#[tokio::test]
async fn env_prefix_reaches_subprocess_then_does_not_leak() {
use kaish_kernel::ast::Value;
use std::collections::HashMap;
let mut vars = HashMap::new();
vars.insert("PATH".to_string(), Value::String("/usr/bin:/bin".into()));
let kernel = Kernel::new(KernelConfig::repl().with_initial_vars(vars))
.expect("Failed to create kernel");
let scoped = kernel
.execute("MY_PROBE=fromprefix printenv MY_PROBE")
.await
.unwrap();
assert!(scoped.ok(), "prefixed printenv should see MY_PROBE: {scoped:?}");
assert_eq!(scoped.text_out().trim(), "fromprefix");
let after = kernel.execute("printenv MY_PROBE").await.unwrap();
assert!(
!after.ok(),
"MY_PROBE must not persist past the prefixed command: {after:?}"
);
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn env_prefix_collection_to_external_is_a_loud_error() {
let kernel = repl_kernel();
let result = kernel.execute("X=[1 2] /bin/true").await;
let msg = match result {
Ok(r) => {
assert_ne!(r.code, 0, "must not spawn with a collection env: {r:?}");
r.err
}
Err(e) => format!("{e:#}"),
};
assert!(msg.contains("tojson"), "should hint at tojson: {msg}");
assert!(msg.contains("list"), "should name the shape: {msg}");
}
fn interactive_kernel() -> Kernel {
Kernel::new(KernelConfig::repl().with_interactive(true)).expect("Failed to create kernel")
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn non_interactive_stdin_is_dev_null() {
let kernel = repl_kernel();
let result = kernel
.execute("/bin/readlink /proc/self/fd/0")
.await
.unwrap();
assert!(result.ok(), "readlink should succeed: {:?}", result);
assert_eq!(
result.text_out().trim(),
"/dev/null",
"Non-interactive external command stdin should be /dev/null: {}",
result.text_out()
);
}
#[cfg(target_os = "linux")]
#[tokio::test]
#[ignore = "requires TTY stdin — fails when cargo test runs with stdin=/dev/null"]
async fn interactive_stdin_is_not_dev_null() {
let kernel = interactive_kernel();
let result = kernel
.execute("/bin/readlink /proc/self/fd/0 | cat")
.await
.unwrap();
assert!(result.ok(), "readlink should succeed: {:?}", result);
assert_ne!(
result.text_out().trim(),
"/dev/null",
"Interactive external command stdin should NOT be /dev/null: {}",
result.text_out()
);
}
#[tokio::test]
async fn interactive_piped_stdin_still_works() {
let kernel = interactive_kernel();
let result = kernel
.execute("echo hello | grep hello")
.await
.unwrap();
assert_eq!(
result.code, 0,
"grep should find 'hello' in piped input (exit 0): {:?}",
result
);
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn external_argv_does_not_split_space_containing_var() {
let kernel = repl_kernel();
kernel.execute(r#"X="a b c""#).await.unwrap();
let result = kernel
.execute(r#"/usr/bin/printf "[%s]" $X"#)
.await
.unwrap();
assert!(result.ok(), "printf should succeed: {:?}", result);
assert_eq!(
result.text_out(),
"[a b c]",
"external argv split a space-containing $VAR"
);
}
#[tokio::test]
async fn minus_alone_lexes_correctly() {
let kernel = repl_kernel();
let result = kernel.execute("echo - foo -").await.unwrap();
assert!(result.ok(), "echo should succeed: {:?}", result);
assert!(result.text_out().contains("- foo -"), "Should include dashes: {}", result.text_out());
}
#[tokio::test]
async fn kill_signals_external_background_job_process_group() {
let kernel = repl_kernel();
kernel
.execute("/usr/bin/sleep 30 &")
.await
.expect("background");
let mut ready = false;
for _ in 0..150 {
if kernel
.execute("kill --signal CONT %1")
.await
.expect("execute")
.code
== 0
{
ready = true;
break;
}
tokio::time::sleep(Duration::from_millis(20)).await;
}
assert!(ready, "background external job never registered its PGID (~3s)");
let result = kernel
.execute("kill --signal STOP %1; kill --signal CONT %1; kill %1")
.await
.expect("execute");
assert_eq!(
result.code, 0,
"STOP/CONT/TERM via process group should all succeed: {}",
result.err
);
let again = kernel.execute("kill %1").await.expect("execute");
assert_eq!(again.code, 0, "re-kill of a killed job is a clean no-op: {}", again.err);
assert!(
again.text_out().contains("already finished (killed:"),
"job must be tracked as killed, got: {}",
again.text_out()
);
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn external_binary_output_is_captured_as_bytes() {
let kernel = repl_kernel();
let r = kernel
.execute(r#"sh -c 'printf "\377\376\375"'"#)
.await
.unwrap();
assert!(r.is_bytes(), "binary external output should be a Bytes result");
assert_eq!(r.out_bytes(), Some(&[0xffu8, 0xfe, 0xfd][..]));
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn external_binary_output_redirects_raw() {
let kernel = repl_kernel();
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("b.bin");
let p = path.to_string_lossy();
let r = kernel
.execute(&format!(r#"sh -c 'printf "\377\376\375\374"' > {p}; dd if={p} of=/dev/null"#))
.await
.unwrap();
assert!(r.err.contains("4 bytes copied"), "raw redirect size: {}", r.err);
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn spawn_forwards_and_captures_binary() {
let kernel = repl_kernel();
let r = kernel
.execute("echo ff | xxd -r -p | spawn --command cat")
.await
.unwrap();
assert!(r.is_bytes(), "binary round-trip through cat should be Bytes");
assert_eq!(r.out_bytes(), Some(&[0xffu8][..]));
}
#[tokio::test]
async fn external_stdout_overflow_is_loud_by_default() {
let kernel = repl_kernel();
let result = kernel
.execute(r#"sh -c "yes x | head -c 11000000""#)
.await
.unwrap();
assert_eq!(
result.code, 3,
"overflow must remap to exit 3 (execute_pipeline's did_spill remap): err={}",
result.err
);
assert!(result.did_spill, "overflow must set did_spill for the exit-3 remap");
assert_eq!(
result.original_code,
Some(0),
"the child's own exit status (0) should be preserved as original_code"
);
assert!(
result.err.contains("stdout truncated"),
"stderr should carry a loud truncation marker: {}",
result.err
);
assert!(
result.err.contains("output-limit"),
"marker should point at the fix (enable output-limit to spill to disk): {}",
result.err
);
let out = result.text_out();
assert!(
out.len() <= kaish_kernel::DEFAULT_STREAM_MAX_SIZE,
"stdout should be capped at the ring size, got {} bytes",
out.len()
);
assert!(
!out.contains("truncated"),
"stdout must not be contaminated with marker text: {:?}",
&out[..out.len().min(80)]
);
assert!(
out.chars().all(|c| c == 'x' || c == '\n'),
"stdout tail should be a clean 'x\\n' repeat, unmodified by the marker"
);
}
#[tokio::test]
async fn external_stdout_within_limit_is_unaffected() {
let kernel = repl_kernel();
let result = kernel.execute(r#"sh -c "printf hello""#).await.unwrap();
assert_eq!(result.code, 0, "err: {}", result.err);
assert!(!result.did_spill, "small output must not trip the overflow signal");
assert_eq!(result.text_out(), "hello");
assert!(
result.err.is_empty(),
"no marker should appear for output within the capture buffer: {}",
result.err
);
}
#[cfg(feature = "overlay")]
#[tokio::test]
async fn external_command_under_overlay_gives_friendly_virtual_cwd_error() {
let dir = tempfile::tempdir().expect("tempdir");
let root = dir.path();
let mut vars = HashMap::new();
vars.insert(
"PATH".to_string(),
Value::String(std::env::var("PATH").unwrap_or_default()),
);
let config = KernelConfig::agent_with_root(root.to_path_buf())
.with_overlay(true)
.with_trash(false)
.with_allow_external_commands(true)
.with_initial_vars(vars);
let kernel = Kernel::new(config).expect("overlay kernel");
let result = kernel.execute("printenv").await.expect("execute");
assert_eq!(
result.code, 127,
"exit code stays 127 — this is a wording-only fix: {result:?}"
);
let msg = format!("{}{}", result.text_out(), result.err);
assert!(msg.contains("printenv"), "error should name the command: {msg}");
assert!(
msg.contains("real filesystem"),
"error should explain the actual cause (no real filesystem location \
for the cwd), not a generic not-found: {msg}"
);
assert!(
!msg.contains("command not found"),
"must not fall back to the generic 'command not found' message \
anymore — `printenv` really is on PATH: {msg}"
);
}
#[cfg(feature = "overlay")]
#[tokio::test]
async fn external_command_not_in_path_under_overlay_stays_generic_not_found() {
let dir = tempfile::tempdir().expect("tempdir");
let root = dir.path();
let mut vars = HashMap::new();
vars.insert(
"PATH".to_string(),
Value::String(std::env::var("PATH").unwrap_or_default()),
);
let config = KernelConfig::agent_with_root(root.to_path_buf())
.with_overlay(true)
.with_trash(false)
.with_allow_external_commands(true)
.with_initial_vars(vars);
let kernel = Kernel::new(config).expect("overlay kernel");
let result = kernel
.execute("definitely-not-a-real-command-181")
.await
.expect("execute");
assert_eq!(result.code, 127, "unresolvable command should still be 127: {result:?}");
let msg = format!("{}{}", result.text_out(), result.err);
assert!(
msg.contains("command not found"),
"a command that isn't in PATH at all must keep the generic \
not-found message, not the virtual-cwd one: {msg}"
);
}
#[tokio::test]
async fn disabled_external_commands_report_the_condition_not_command_not_found() {
assert!(
std::path::Path::new("/bin/sh").exists(),
"test fixture requires /bin/sh to exist on this system"
);
let config = KernelConfig::repl().with_allow_external_commands(false);
let kernel = Kernel::new(config).expect("kernel with external commands disabled");
let result = kernel.execute("/bin/sh -c true").await.expect("execute");
assert_eq!(result.code, 127, "policy refusal keeps the not-found exit code: {result:?}");
let msg = format!("{}{}", result.text_out(), result.err);
assert!(
!msg.contains("command not found"),
"a resolvable command refused by policy must not be misreported as missing: {msg}"
);
assert!(
msg.contains("external commands are disabled on this shell"),
"the refusal should name the actual condition: {msg}"
);
}
#[tokio::test]
async fn enabled_external_commands_still_report_not_found_for_a_missing_command() {
let kernel = repl_kernel();
let result = kernel
.execute("definitely-not-a-real-command-disabled-reason-fix")
.await
.expect("execute");
assert_eq!(result.code, 127, "not found is still exit 127: {result:?}");
let msg = format!("{}{}", result.text_out(), result.err);
assert!(
msg.contains("command not found"),
"a genuinely missing command must keep the generic not-found message: {msg}"
);
assert!(
!msg.contains("external commands are disabled"),
"must not claim policy refusal when the command simply isn't on PATH: {msg}"
);
}
#[cfg(target_os = "linux")]
#[tokio::test]
async fn an_external_command_after_a_partial_read_sees_the_remainder() {
use kaish_kernel::{pipe_stream_default, ExecuteOptions};
let (writer, reader) = pipe_stream_default();
writer.write_bytes(b"first\nsecond\nthird\n").await.unwrap();
drop(writer);
let kernel = repl_kernel();
let result = kernel
.execute_with_pipe_stdin("read x; /bin/cat", ExecuteOptions::new(), reader)
.await
.expect("kernel execute");
assert_eq!(result.code, 0, "cat should succeed: {}", result.err);
assert_eq!(
result.text_out(),
"second\nthird\n",
"the external command must resume where `read` stopped"
);
}
#[tokio::test]
async fn env_cannot_bypass_the_external_commands_gate() {
let kernel = Kernel::new(KernelConfig::isolated()).expect("kernel");
let direct = kernel.execute("/bin/echo MARKER").await.expect("exec");
assert_ne!(direct.code, 0, "direct external must be refused");
assert!(!direct.text_out().contains("MARKER"));
let via_env = kernel
.execute("env FOO=bar /bin/echo MARKER")
.await
.expect("exec");
assert!(
!via_env.text_out().contains("MARKER"),
"SANDBOX BYPASS: env ran a host binary with external commands disabled: {via_env:?}"
);
assert_ne!(via_env.code, 0, "env must refuse, not silently succeed");
assert!(
via_env.err.contains("external commands are"),
"the refusal must name the condition: {:?}",
via_env.err
);
}