{"record":{"id":"5fa1b7dd8e06d0df","repo":"BoundaryML/baml","slug":"substring-end-argument-must-be-an-int-at","errorCode":null,"errorMessage":"substring() end argument must be an int at {:?}","messagePattern":"substring\\(\\) end argument must be an int at (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/baml-compiler/src/thir/interpret.rs","lineNumber":2961,"sourceCode":"        }\n        \"substring\" => {\n            let BamlValueWithMeta::String(s, _) = receiver else {\n                bail!(\n                    \"substring() method only available on strings at {:?}\",\n                    meta.0\n                );\n            };\n            if args.len() != 2 {\n                bail!(\n                    \"substring() method takes exactly 2 arguments at {:?}\",\n                    meta.0\n                );\n            }\n            let BamlValueWithMeta::Int(start, _) = &args[0] else {\n                bail!(\"substring() start argument must be an int at {:?}\", meta.0);\n            };\n            let BamlValueWithMeta::Int(end, _) = &args[1] else {\n                bail!(\"substring() end argument must be an int at {:?}\", meta.0);\n            };\n\n            let start = (*start as usize).min(s.len());\n            let end = (*end as usize).min(s.len()).max(start);\n\n            Ok(BamlValueWithMeta::String(\n                s[start..end].to_string(),\n                meta.clone(),\n            ))\n        }\n        \"replace\" => {\n            let BamlValueWithMeta::String(s, _) = receiver else {\n                bail!(\"replace() method only available on strings at {:?}\", meta.0);\n            };\n            if args.len() != 2 {\n                bail!(\"replace() method takes exactly 2 arguments at {:?}\", meta.0);\n            }\n            let BamlValueWithMeta::String(search, _) = &args[0] else {","sourceCodeStart":2943,"sourceCodeEnd":2979,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-compiler/src/thir/interpret.rs#L2943-L2979","documentation":"The second argument to substring (the end index) must be an integer. The interpreter destructures args[1] as BamlValueWithMeta::Int and bails with this message otherwise. The value is later clamped with .min(s.len()).max(start), but its type must still be Int.","triggerScenarios":"Calling s.substring(0, \"5\"), s.substring(0, endFloat), or passing a null/non-int expression as the end index in a BAML expression.","commonSituations":"End indices coming from string-typed parsed values; float results from division/arithmetic used as the end; optional end variables that are null at runtime.","solutions":["Pass an integer as the end index; convert string/float values to int before the call.","Check the type of the expression in argument position 1 at the failing call site.","Note that out-of-bounds ends are already clamped, so only the type needs fixing — pass a large int to mean 'to the end'."],"exampleFix":"// before\nlet head = s.substring(0, endStr); // endStr: string\n\n// after\nlet head = s.substring(0, endStr.to_int());","handlingStrategy":"validation","validationCode":"// end index must be an int\nlet end_ok = end is int;","typeGuard":"fn is_int(v: BamlValue) -> bool { matches!(v, BamlValue::Int(_)) }","tryCatchPattern":"match result {\n  Err(e) if e.to_string().contains(\"end argument must be an int\") => use_string_len_as_end,\n  other => other,\n}","preventionTips":["Round/convert computed end positions to int before the call.","Remember end is clamped to the string length, so a large int is a safe 'to the end' value.","Type-annotate end index variables as int."],"tags":["baml","argument-type","substring","index"],"backgroundTag":"type-mismatch","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}