{"record":{"id":"4b22ff5fa6546c82","repo":"mvanhorn/last30days-skill","slug":"s-must-be-a-boolean","errorCode":null,"errorMessage":"%s must be a boolean","messagePattern":"(.+?) must be a boolean","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mcp/internal/tools/research.go","lineNumber":143,"sourceCode":"\t}\n\tswitch value {\n\tcase \"\":\n\t\treturn \"compact\", nil\n\tcase \"compact\", \"html\":\n\t\treturn value, nil\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"emit must be 'compact' or 'html', got %q\", value)\n\t}\n}\n\nfunc boolArgument(args map[string]any, name string) (bool, error) {\n\traw, ok := args[name]\n\tif !ok {\n\t\treturn false, nil\n\t}\n\tvalue, ok := raw.(bool)\n\tif !ok {\n\t\treturn false, fmt.Errorf(\"%s must be a boolean\", name)\n\t}\n\treturn value, nil\n}\n\n// formatRunError flattens engine.Run's distinct error shapes into a single\n// user-facing message that includes the relevant stderr context.\nfunc formatRunError(runErr error, res *engine.RunResult) string {\n\tvar msg strings.Builder\n\tmsg.WriteString(runErr.Error())\n\tif res != nil && len(res.Stderr) > 0 {\n\t\tmsg.WriteString(\"\\nengine stderr:\\n\")\n\t\tmsg.Write(res.Stderr)\n\t}\n\treturn msg.String()\n}\n","sourceCodeStart":125,"sourceCodeEnd":159,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/mcp/internal/tools/research.go#L125-L159","documentation":"Thrown by boolArgument (mcp/internal/tools/research.go:143) when an optional boolean argument is present but its type is not a Go bool. On the 'research' tool the only boolean is 'save', so the message reads \"save must be a boolean\". Because the argument is optional, omission is fine (defaults to false); the error fires only when a value IS supplied and JSON-decodes to a string (\"true\"), number (1/0), or null-like non-bool. There is no string-coercion: the server requires a real JSON boolean.","triggerScenarios":"Calling 'research' with {\"save\": \"true\"}, {\"save\": \"false\"}, {\"save\": 1}, or {\"save\": 0} instead of a bare JSON true/false. Typical when arguments are built with string formatting instead of a JSON encoder, when a model quotes the boolean, or when a config-driven pipeline stores flags as strings.","commonSituations":"Quoted booleans from LLM tool calls ({\"save\": \"true\"}); templates that interpolate booleans into JSON text; configs/env vars carrying \"1\"/\"0\" or \"yes\"/\"no\" being forwarded verbatim; strongly-typed clients mapping ints to the flag.","solutions":["Pass a real JSON boolean: {\"save\": true} or {\"save\": false} — no quotes, no 1/0.","If the flag comes from a string env var or config, convert it yourself before the call: save := flagStr == \"1\" || strings.EqualFold(flagStr, \"true\").","Build the arguments map with Go bools (map[string]any{\"save\": true}) or a real JSON serializer so booleans stay unquoted."],"exampleFix":"// before (string-interpolated JSON)\nbody := fmt.Sprintf(`{\"topic\":\"x\",\"save\":\"%t\"}`, saveFlag)\n// -> error: save must be a boolean\n\n// after (typed map serialized with encoding/json)\nargs := map[string]any{\"topic\": \"x\", \"save\": saveFlag} // saveFlag is bool\npayload, _ := json.Marshal(args)","handlingStrategy":"type-guard","validationCode":"// Coerce loose flag representations to a real bool before dispatch.\nfunc toBool(v any) (bool, error) {\n\tswitch t := v.(type) {\n\tcase nil:\n\t\treturn false, nil\n\tcase bool:\n\t\treturn t, nil\n\tcase string:\n\t\tswitch strings.ToLower(t) {\n\t\tcase \"true\", \"1\", \"yes\":\n\t\t\treturn true, nil\n\t\tcase \"false\", \"0\", \"no\", \"\":\n\t\t\treturn false, nil\n\t\t}\n\t}\n\treturn false, fmt.Errorf(\"cannot use %v as boolean for save\", v)\n}","typeGuard":"func isBool(v any) bool {\n\t_, ok := v.(bool)\n\treturn ok\n}","tryCatchPattern":null,"preventionTips":["Never build tool-call JSON with fmt.Sprintf — use typed maps plus encoding/json so booleans stay unquoted.","When the flag originates from env vars or config files (\"1\"/\"0\", \"true\"/\"false\"), convert to bool in your code before forwarding.","Omit optional booleans you don't need; absence defaults to false and cannot trigger this error."],"tags":["go","mcp","validation","type-mismatch","boolean","arguments"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}