Skip to content

Commit 2678fa0

Browse files
committed
fix: ultraworkers#124 --model validation rejects malformed syntax at parse time
Adds validate_model_syntax() that rejects: - Empty strings - Strings with spaces (e.g., 'bad model') - Invalid provider/model format Accepts: - Known aliases (opus, sonnet, haiku) - Valid provider/model format (provider/model) Wired into parse_args for both --model <value> and --model=<value> forms. Errors exit with clear message before any API calls (no token burn). Verified: - 'claw --model "bad model" version' → error, exit 1 - 'claw --model "" version' → error, exit 1 - 'claw --model opus version' → works - 'claw --model anthropic/claude-opus-4-6 version' → works Refs: ROADMAP ultraworkers#124 (debbcbe cluster — parser-level trust gap family)
1 parent b9990bb commit 2678fa0

1 file changed

Lines changed: 35 additions & 1 deletion

File tree

  • rust/crates/rusty-claude-cli/src

rust/crates/rusty-claude-cli/src/main.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,11 +447,14 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
447447
let value = args
448448
.get(index + 1)
449449
.ok_or_else(|| "missing value for --model".to_string())?;
450+
validate_model_syntax(value)?;
450451
model = resolve_model_alias_with_config(value);
451452
index += 2;
452453
}
453454
flag if flag.starts_with("--model=") => {
454-
model = resolve_model_alias_with_config(&flag[8..]);
455+
let value = &flag[8..];
456+
validate_model_syntax(value)?;
457+
model = resolve_model_alias_with_config(value);
455458
index += 1;
456459
}
457460
"--output-format" => {
@@ -1035,6 +1038,37 @@ fn resolve_model_alias_with_config(model: &str) -> String {
10351038
resolve_model_alias(trimmed).to_string()
10361039
}
10371040

1041+
/// Validate model syntax at parse time.
1042+
/// Accepts: known aliases (opus, sonnet, haiku) or provider/model pattern.
1043+
/// Rejects: empty, whitespace-only, strings with spaces, or invalid chars.
1044+
fn validate_model_syntax(model: &str) -> Result<(), String> {
1045+
let trimmed = model.trim();
1046+
if trimmed.is_empty() {
1047+
return Err("model string cannot be empty".to_string());
1048+
}
1049+
// Known aliases are always valid
1050+
match trimmed {
1051+
"opus" | "sonnet" | "haiku" => return Ok(()),
1052+
_ => {}
1053+
}
1054+
// Check for spaces (malformed)
1055+
if trimmed.contains(' ') {
1056+
return Err(format!(
1057+
"invalid model syntax: '{}' contains spaces. Use provider/model format or known alias",
1058+
trimmed
1059+
));
1060+
}
1061+
// Check provider/model format: provider_id/model_id
1062+
let parts: Vec<&str> = trimmed.split('/').collect();
1063+
if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
1064+
return Err(format!(
1065+
"invalid model syntax: '{}'. Expected provider/model (e.g., anthropic/claude-opus-4-6) or known alias (opus, sonnet, haiku)",
1066+
trimmed
1067+
));
1068+
}
1069+
Ok(())
1070+
}
1071+
10381072
fn config_alias_for_current_dir(alias: &str) -> Option<String> {
10391073
if alias.is_empty() {
10401074
return None;

0 commit comments

Comments
 (0)