mvanhorn/last30days-skill · error
%s must be a non-empty string
Error message
%s must be a non-empty string
What it means
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.
Source
Thrown at mcp/internal/tools/research.go:112
return runArgs
}
func mcpSaveDir() string {
saveDir := os.Getenv("LAST30DAYS_MEMORY_DIR")
if saveDir == "" {
return "~/Documents/Last30Days"
}
return saveDir
}
func requireString(args map[string]any, name string) (string, error) {
raw, ok := args[name]
if !ok {
return "", fmt.Errorf("%s is required", name)
}
value, ok := raw.(string)
if !ok || strings.TrimSpace(value) == "" {
return "", fmt.Errorf("%s must be a non-empty string", name)
}
return value, nil
}
func emitArgument(args map[string]any) (string, error) {
raw, ok := args["emit"]
if !ok {
return "compact", nil
}
value, ok := raw.(string)
if !ok {
return "", errors.New("emit must be a string")
}
switch value {
case "":
return "compact", nil
case "compact", "html":
return value, nilView on GitHub (pinned to c7460f6114)
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.
Example fix
// before
args := map[string]any{"topic": strings.TrimSpace(userInput)} // userInput was " "
// -> error: topic must be a non-empty string
// after
topic := strings.TrimSpace(userInput)
if topic == "" {
return errors.New("ask the user for a topic before calling research")
}
args := map[string]any{"topic": topic} Defensive patterns
Strategy: validation
Validate before calling
// Reject empty/whitespace or non-string topics before dispatch.
func validTopic(v any) bool {
s, ok := v.(string)
return ok && strings.TrimSpace(s) != ""
}
if !validTopic(args["topic"]) {
return errors.New("provide a non-empty research topic")
} Type guard
func isNonEmptyString(v any) bool {
s, ok := v.(string)
return ok && strings.TrimSpace(s) != ""
} Prevention
- 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).
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- format must be 'text' or 'json', got %q
- %s is required
- emit must be 'compact' or 'html', got %q
- %s must be a boolean
- Unknown search source in {flag_name}: {source}
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/2114eb68e174a2d6.
Report an issue: GitHub.