the-benchmarker/web-frameworks · critical
Failed to parse app.zl
Error message
Failed to parse app.zl
What it means
zeno-rs-actix's main loads app.zl (from disk, falling back to the copy compiled in via include_str!) and parses it with parse_string; the .expect panics with "Failed to parse app.zl" if the Zeno DSL source is syntactically invalid, aborting server startup before the route map can be built.
Solutions
- Run the parser/CLI directly on app.zl to get the line/column of the syntax error and fix the DSL source
- Diff app.zl against the last known-good version (git diff) to spot accidental edits or merge remnants
- If a stale app.zl sits in the working directory, delete or fix it so the valid include_str! fallback is not shadowed by a broken file
- After upgrading the engine crate, update app.zl to the current DSL grammar
- Temporarily replace expect with error propagation/logging that prints the parse diagnostic before exiting
Example fix
// before
let main_node = parse_string(&zl_content, "app.zl").expect("Failed to parse app.zl");
// after
let main_node = parse_string(&zl_content, "app.zl")
.unwrap_or_else(|e| {
eprintln!("app.zl parse error: {e}");
std::process::exit(1);
}); Defensive patterns
Strategy: validation
Validate before calling
// before starting the server, syntax-check app.zl
match std::fs::read_to_string("app.zl")
.or_else(|_| Ok::<_, std::io::Error>(include_str!("../app.zl").to_string()))
{
Ok(content) => {
if let Err(e) = zeno::parse_string(&content, "app.zl") {
eprintln!("app.zl is invalid, refusing to start: {e:?}");
std::process::exit(1);
}
}
Err(e) => {
eprintln!("cannot read app.zl: {e}");
std::process::exit(1);
}
} Type guard
null
Try / catch
// main panics on parse failure; if embedding, isolate the parse step
let main_node = match std::panic::catch_unwind(|| parse_string(&zl_content, "app.zl")) {
Ok(Ok(node)) => node,
Ok(Err(e)) => { eprintln!("app.zl parse error: {e:?}"); std::process::exit(1); }
Err(_) => { eprintln!("parser panicked while parsing app.zl"); std::process::exit(1); }
}; Prevention
- Add a CI step that parses app.zl on every commit
- Validate app.zl after merges and before building deploy images
- Keep app.zl under version control and review all edits to it
- After engine upgrades, re-validate app.zl against the new DSL grammar
- Prefer a validated baked-in app.zl over an unvetted file dropped into the runtime directory
When it happens
Trigger: Running the actix server binary when app.zl in the working directory (or the bundled ../app.zl fallback) contains DSL syntax errors, or when the file is read successfully but parse_string returns an Err for the 'app.zl' source name.
Common situations: A developer hand-edited app.zl and introduced a syntax error; an incomplete merge left the file malformed; the wrong app.zl was copied into the container working directory, shadowing a valid build-time one; DSL grammar changed in a library upgrade so older .zl files no longer parse.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of the-benchmarker/web-frameworks@3795a31d72 (2026-09-15).
Data as JSON: /api/errors/5c3b408d67160031.
Report an issue: GitHub.
Appendix: source
Thrown at rust/zeno-rs-actix/src/main.rs:174
let r_post = routes.clone();
engine.register(
"http.post",
Arc::new(move |_, _, node, _| {
let raw = node.value.clone().unwrap_or_default().trim().to_string();
let clean = if raw.starts_with('\'') || raw.starts_with('"') {
raw[1..raw.len() - 1].to_string()
} else {
raw
};
r_post.lock().unwrap().push(("POST".to_string(), clean, node.clone()));
Ok(())
}),
empty_slot_meta(),
);
let zl_content = std::fs::read_to_string("app.zl")
.unwrap_or_else(|_| include_str!("../app.zl").to_string());
let main_node = parse_string(&zl_content, "app.zl").expect("Failed to parse app.zl");
let parent_scope = Scope::new(None);
let mut init_ctx = Context::new();
let _ = engine.execute(&mut init_ctx, &main_node, &parent_scope);
let mut route_map: HashMap<String, MethodHandler> = HashMap::new();
for (method, path, node) in routes.lock().unwrap().drain(..) {
let matchit_path = convert_path_to_matchit(&path);
println!("Registered route: {} {} -> matchit: {}", method, path, matchit_path);
let entry = route_map
.entry(matchit_path)
.or_insert(MethodHandler { get: None, post: None });
if method == "GET" {
entry.get = Some(node);
} else if method == "POST" {
entry.post = Some(node);
}
}View on GitHub (pinned to 3795a31d72)