windmill-labs/windmill · error
Impossible to parse arg digit
Error message
Impossible to parse arg digit
What it means
While building the Bash signature, the parser maps each captured positional index (`$1`, `${2}`, `${3:-default}`) to an i32 via capture groups 2/3/4 of RE_BASH. If none of those groups is present or the digits can't parse, it errors with this message — an inconsistency between the regex alternations and the capture-conversion code, normally not user-triggerable.
Source
Thrown at backend/parsers/windmill-parser-bash/src/lib.rs:119
})
} else {
Err(anyhow!("Error parsing powershell script".to_string()))
}
}
lazy_static::lazy_static! {
static ref RE_BASH: Regex = Regex::new(r#"(?m)^(\w+)="\$(?:(\d+)|\{(\d+)\}|\{(\d+):-(.*)\})"(?:[\t ]*)?(?:#.*)?\r?$"#).unwrap();
}
fn parse_bash_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
let mut hm: HashMap<i32, (String, Option<String>)> = HashMap::new();
for cap in RE_BASH.captures_iter(code) {
hm.insert(
cap.get(2)
.or(cap.get(3))
.or(cap.get(4))
.and_then(|x| x.as_str().parse::<i32>().ok())
.ok_or_else(|| anyhow!("Impossible to parse arg digit"))?,
(
cap[1].to_string(),
cap.get(5).map(|x| x.as_str().to_string()),
),
);
}
let mut args = vec![];
for i in 1..20 {
if hm.contains_key(&i) {
let (name, default) = hm.get(&i).unwrap();
args.push(Arg {
name: name.clone(),
typ: Typ::Str(None),
default: default.clone().map(|x| json!(x)),
otyp: None,
has_default: default.is_some(),
oidx: None,
otyp_inferred: false,View on GitHub (pinned to e474e8803c)
Solutions
- Rewrite the offending `$N`-style line in the script to the canonical `name="$1"` form
- If RE_BASH was patched, extend the capture chain (`.or(cap.get(N))`) and the i32 parse to cover the new alternation
- Report upstream if a plain `var="$1"` line triggers it — likely a parser bug
Example fix
// before .and_then(|x| x.as_str().parse::<i32>().ok()) // after (when a new capture group 6 was added to RE_BASH) .or(cap.get(6)) .and_then(|x| x.as_str().parse::<i32>().ok())
Defensive patterns
Strategy: type-guard
Validate before calling
// only submit scripts with canonical arg assignments
const canon = /^\w+="\$(?:\d+|\{\d+\}|\{\d+:-.*\})"/m;
if (!canon.test(code)) throw new Error('no canonical bash positional args'); Type guard
fn has_canonical_bash_args(code: &str) -> bool {
lazy_static::lazy_static! {
static ref RE: regex::Regex = regex::Regex::new(
r#"^\w+="\$(?:\d+|\{\d+\}|\{\d+:-.*\})""#).unwrap();
}
RE.is_match(code)
} Prevention
- Keep positional-arg assignments in the canonical form the regex documents
- Any change to RE_BASH must update the capture-or-chain and add a test
- Keep the regex and the numeric conversion adjacent so drift is visible in review
When it happens
Trigger: A script line matched RE_BASH where groups 2/3/4 are all None or non-numeric — practically triggered by regex/code drift (an alternation added to the regex whose digit capture isn't in the `.or(...)` chain) or exotic input matching an uncovered alternation.
Common situations: Custom forks or patches to RE_BASH adding new alternations without extending the `.get(2).or(get(3)).or(get(4))` chain; scripts with oddly formatted arg lines hitting an edge alternation.
Related errors
- Error parsing bash script
- Error parsing sql
- Could not synthesize ${type} draft "${path}".
- Invalid group
- ${inferedSchema.error}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/3f37df0ba2f5dc6e.
Report an issue: GitHub.