mvanhorn/last30days-skill · error
format must be 'text' or 'json', got %q
Error message
format must be 'text' or 'json', got %q
What it means
Thrown by preflightFormatArgument (mcp/internal/tools/preflight.go:83) when the 'preflight' MCP tool is called with a 'format' argument that is a string but not one of the allowed values. The switch accepts "" (treated as "text"), "text", and "json"; anything else — "Text", "JSON", "md", "yaml" — hits the default case. The error quotes the offending value with %q so the caller sees exactly what the server received. Non-string types are caught earlier by a separate 'format must be a string' error.
Source
Thrown at mcp/internal/tools/preflight.go:83
return runArgs
}
func preflightFormatArgument(args map[string]any) (string, error) {
raw, ok := args["format"]
if !ok {
return "text", nil
}
value, ok := raw.(string)
if !ok {
return "", errors.New("format must be a string")
}
switch value {
case "", "text":
return "text", nil
case "json":
return "json", nil
default:
return "", fmt.Errorf("format must be 'text' or 'json', got %q", value)
}
}
View on GitHub (pinned to c7460f6114)
Solutions
- Pass format: "json" or format: "text" exactly (lowercase), or omit the argument entirely — omission defaults to "text".
- If you want structured machine-readable preflight output, use "json"; there is no markdown/html option on this tool.
- Check for case or whitespace typos in the argument; the comparison is exact and case-sensitive.
- If you're a client author, read the tool's inputSchema (declared via mcplib.WithString("format", ...)) instead of hardcoding guesses.
Example fix
// before (MCP tool call)
{"name": "preflight", "arguments": {"format": "markdown"}}
// -> error: format must be 'text' or 'json', got "markdown"
// after
{"name": "preflight", "arguments": {"format": "json"}} Defensive patterns
Strategy: validation
Validate before calling
// Validate before calling the preflight tool.
func normalizePreflightFormat(v any) (string, error) {
if v == nil {
return "text", nil
}
s, ok := v.(string)
if !ok {
return "", errors.New("format must be a string")
}
switch s {
case "", "text", "json":
return s, nil
}
return "", fmt.Errorf("unsupported preflight format %q (want text|json)", s)
} Type guard
func isPreflightFormat(v any) bool {
switch v {
case nil, "", "text", "json":
return true
}
return false
} Prevention
- Keep the enum in one constant list shared by client and server; never hardcode 'markdown' or other guessed values.
- Remember preflight uses format: text|json while research uses emit: compact|html — don't cross the two vocabularies.
- Values are case-sensitive; always lowercase.
- Read allowed values from the tool's inputSchema instead of inferring them from the description text.
When it happens
Trigger: Calling the MCP 'preflight' tool with format set to any string other than '', 'text', or 'json' — e.g. {"format": "markdown"}, {"format": "Json"} (case-sensitive), or {"format": "txt"}. Common when an LLM client guesses an output format the tool doesn't offer, or when a script copies a --emit value (like 'compact' or 'html', which belong to the research tool) into preflight's format argument.
Common situations: Model-driven MCP clients inventing plausible enum values; reusing the research tool's emit vocabulary ('compact'/'html') on the preflight tool; case mismatches ('JSON' vs 'json'); stale client code written against an older or imagined schema.
Related errors
- emit must be 'compact' or 'html', got %q
- %s is required
- %s must be a non-empty string
- %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/d06a729d4894ae7a.
Report an issue: GitHub.