{"record":{"id":"2114eb68e174a2d6","repo":"mvanhorn/last30days-skill","slug":"s-must-be-a-non-empty-string","errorCode":null,"errorMessage":"%s must be a non-empty string","messagePattern":"(.+?) must be a non-empty string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mcp/internal/tools/research.go","lineNumber":112,"sourceCode":"\treturn runArgs\n}\n\nfunc mcpSaveDir() string {\n\tsaveDir := os.Getenv(\"LAST30DAYS_MEMORY_DIR\")\n\tif saveDir == \"\" {\n\t\treturn \"~/Documents/Last30Days\"\n\t}\n\treturn saveDir\n}\n\nfunc requireString(args map[string]any, name string) (string, error) {\n\traw, ok := args[name]\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"%s is required\", name)\n\t}\n\tvalue, ok := raw.(string)\n\tif !ok || strings.TrimSpace(value) == \"\" {\n\t\treturn \"\", fmt.Errorf(\"%s must be a non-empty string\", name)\n\t}\n\treturn value, nil\n}\n\nfunc emitArgument(args map[string]any) (string, error) {\n\traw, ok := args[\"emit\"]\n\tif !ok {\n\t\treturn \"compact\", nil\n\t}\n\tvalue, ok := raw.(string)\n\tif !ok {\n\t\treturn \"\", errors.New(\"emit must be a string\")\n\t}\n\tswitch value {\n\tcase \"\":\n\t\treturn \"compact\", nil\n\tcase \"compact\", \"html\":\n\t\treturn value, nil","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/mcp/internal/tools/research.go#L94-L130","documentation":"Thrown by requireString (mcp/internal/tools/research.go:112) when the required argument key is present but is either not a Go string (e.g. a JSON number, object, or bool) or is a string that trims to empty (whitespace-only counts as empty). For the 'research' tool this means \"topic must be a non-empty string\". The TrimSpace check means \"   \" fails just like \"\" — the server refuses to spawn a research subprocess for a topic with no content.","triggerScenarios":"Calling 'research' with {\"topic\": \"\"} or {\"topic\": \"   \"} or {\"topic\": 42}/{\"topic\": {\"q\": \"...\"}}. Typical sources: a model passing an empty topic when the user gave none; template interpolation producing an empty string ({{user_query}} with no input); JSON numbers for numeric-sounding topics; forwarding an unvalidated upstream field.","commonSituations":"Prompt-template pipelines where the topic variable is empty; UI clients sending the form state before the user typed anything; models echoing a structured query object instead of a plain string; whitespace-only input from copy-paste.","solutions":["Send a real topic string: {\"topic\": \"CUDA memory management\"}.","Validate upstream: if strings.TrimSpace(topic) == \"\", prompt the user for a topic instead of calling the tool.","Make sure the value is a JSON string, not a number/object — quote numeric-sounding topics like \"42\".","In client code, trim and check the topic before dispatch so your error message can be more contextual than the server's."],"exampleFix":"// before\nargs := map[string]any{\"topic\": strings.TrimSpace(userInput)} // userInput was \"   \"\n// -> error: topic must be a non-empty string\n\n// after\ntopic := strings.TrimSpace(userInput)\nif topic == \"\" {\n    return errors.New(\"ask the user for a topic before calling research\")\n}\nargs := map[string]any{\"topic\": topic}","handlingStrategy":"validation","validationCode":"// Reject empty/whitespace or non-string topics before dispatch.\nfunc validTopic(v any) bool {\n\ts, ok := v.(string)\n\treturn ok && strings.TrimSpace(s) != \"\"\n}\n\nif !validTopic(args[\"topic\"]) {\n    return errors.New(\"provide a non-empty research topic\")\n}","typeGuard":"func isNonEmptyString(v any) bool {\n\ts, ok := v.(string)\n\treturn ok && strings.TrimSpace(s) != \"\"\n}","tryCatchPattern":null,"preventionTips":["Trim user input and check for empty before calling research; prompt the user instead of forwarding blank strings.","Ensure template variables are populated — an empty interpolation yields \"\" and fails server-side.","Send topics as JSON strings; quote numeric-looking topics (\"42\", not 42)."],"tags":["go","mcp","validation","empty-string","arguments"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}