{"record":{"id":"66aa1169b69d913d","repo":"zeroclaw-labs/zeroclaw","slug":"screenshot-path-parameter-must-be-a-string-got","errorCode":null,"errorMessage":"Screenshot 'path' parameter must be a string, got { $path }","messagePattern":"Screenshot 'path' parameter must be a string, got (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/browser.rs","lineNumber":1030,"sourceCode":"                // String: validate against workspace through the one canonical\n                // validator shared with the local backends.\n                let mut args = args;\n                let resolved_target = self.validate_screenshot_target(path_str).await?;\n\n                // Store the validated path for local write after sidecar returns PNG.\n                // Do NOT forward the path to the sidecar - it returns PNG bytes.\n                if let Some(obj) = args.as_object_mut() {\n                    obj.insert(\"path\".to_string(), Value::String(resolved_target));\n                }\n                Ok(args)\n            }\n            Some(_) => {\n                // NonString: integer, array, object → reject\n                let msg = crate::i18n::get_required_tool_string_with_args(\n                    \"tool-browser-screenshot-error-computeruse-non-string-path\",\n                    &[(\"path\", &format!(\"{path:?}\"))],\n                );\n                anyhow::bail!(\"{msg}\");\n            }\n        }\n    }\n\n    async fn execute_computer_use_action(\n        &self,\n        action: &str,\n        args: &Value,\n    ) -> anyhow::Result<ToolResult> {\n        let endpoint = self.computer_use_endpoint_url()?;\n\n        // Validate screenshot path but do NOT forward it to the sidecar.\n        // The sidecar returns PNG bytes, and we perform the validated local write.\n        let validated_path = if action == \"screenshot\" {\n            match self\n                .validate_screenshot_path_for_computer_use(action, args.clone())\n                .await\n            {","sourceCodeStart":1012,"sourceCodeEnd":1048,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/browser.rs#L1012-L1048","documentation":"In the computer-use flow, the screenshot `path` argument is classified as Absent, String, or NonString. Null, missing, or empty-string means 'return the PNG inline'; a string is validated as a write target; every other JSON type (integer, array, object, boolean) hits the NonString arm and is rejected with the debug-formatted value. The strictness exists because the validated value is later used for a local filesystem write.","triggerScenarios":"Sending {\"action\": \"screenshot\", \"path\": 0}, {\"path\": [\"shot.png\"]}, or {\"path\": {\"file\": \"shot.png\"}} to the browser tool with computer-use configured.","commonSituations":"An LLM emits a numeric flag or a structured object where a path string was expected; upstream code forwards unvalidated JSON; a model confuses the numeric `pixels` parameter with `path`.","solutions":["Pass path as a JSON string (\"screenshots/shot.png\") or omit it entirely to get the PNG inline","Enforce the tool's parameters schema (type: string) before dispatch","Treat empty string as 'inline PNG' if no file write is wanted"],"exampleFix":"// before\n{\"action\": \"screenshot\", \"path\": 1}\n// after\n{\"action\": \"screenshot\", \"path\": \"screenshots/shot.png\"}","handlingStrategy":"type-guard","validationCode":"if !matches!(args.get(\"path\"), None | Some(serde_json::Value::Null) | Some(serde_json::Value::String(_))) {\n    return Err(\"screenshot 'path' must be a string, null, or absent\".into());\n}","typeGuard":"fn is_valid_screenshot_path_param(args: &serde_json::Value) -> bool {\n    match args.get(\"path\") {\n        None | Some(serde_json::Value::Null) => true,\n        Some(serde_json::Value::String(_)) => true,\n        _ => false,\n    }\n}","tryCatchPattern":"match tool.execute(args).await {\n    Ok(res) if res.success => { /* ... */ }\n    Ok(res) => { /* res.error carries the rejection; fix the arg type, not the environment */ }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate tool arguments against parameters_schema before dispatch","Keep path parameters as plain strings end to end","Log rejected argument shapes to spot schema drift in callers"],"tags":["computer-use","screenshot","json","type-validation","arguments"],"backgroundTag":"tool-argument-type-mismatch","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}