mvanhorn/last30days-skill · error
emit must be 'compact' or 'html', got %q
Error message
emit must be 'compact' or 'html', got %q
What it means
Thrown by emitArgument (mcp/internal/tools/research.go:132) when the 'research' tool's optional 'emit' argument is a string outside the allowed set. The switch accepts "" (treated as "compact"), "compact", and "html"; any other value — "md", "text", "json", "HTML" — hits the default case and the offending value is echoed with %q. This is the research tool's counterpart to preflight's format enum; the two vocabularies differ on purpose (research emits compact/html artifacts, preflight emits text/json summaries), which is a common mix-up.
Source
Thrown at mcp/internal/tools/research.go:132
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, nil
default:
return "", fmt.Errorf("emit must be 'compact' or 'html', got %q", value)
}
}
func boolArgument(args map[string]any, name string) (bool, error) {
raw, ok := args[name]
if !ok {
return false, nil
}
value, ok := raw.(bool)
if !ok {
return false, fmt.Errorf("%s must be a boolean", name)
}
return value, nil
}
// formatRunError flattens engine.Run's distinct error shapes into a single
// user-facing message that includes the relevant stderr context.
func formatRunError(runErr error, res *engine.RunResult) string {View on GitHub (pinned to c7460f6114)
Solutions
- Use emit: "compact" (default, inline synthesis) or emit: "html" (also saves a shareable HTML brief) — or omit emit entirely.
- Don't pass 'json' or 'text' to research; those belong to the preflight tool's format argument.
- Check casing — the comparison is case-sensitive and exact.
- Client authors: derive allowed values from the tool's inputSchema description rather than hardcoding.
Example fix
// before
{"name": "research", "arguments": {"topic": "rust 2026", "emit": "markdown"}}
// -> error: emit must be 'compact' or 'html', got "markdown"
// after
{"name": "research", "arguments": {"topic": "rust 2026", "emit": "html"}} Defensive patterns
Strategy: validation
Validate before calling
// Validate emit before calling research.
func validEmit(v any) bool {
switch v {
case nil, "", "compact", "html":
return true
}
return false
}
if !validEmit(args["emit"]) {
args["emit"] = "compact" // or reject, per your policy
} Type guard
func isResearchEmit(v any) bool {
switch v {
case nil, "", "compact", "html":
return true
}
return false
} Prevention
- Remember the split: research takes emit=compact|html; preflight takes format=text|json. 'json' and 'text' are invalid for research.
- Defaulting to omitting emit is safest — the server treats missing as compact.
- Keep enums lowercase and exact; no case-insensitive matching is done.
When it happens
Trigger: Calling 'research' with {"emit": "json"}, {"emit": "text"}, {"emit": "markdown"}, or {"emit": "HTML"}. Most often a model or script reuses the preflight tool's format values ('text'/'json') on research, guesses 'markdown' for a report, or gets the case wrong.
Common situations: Cross-tool vocabulary confusion between preflight's format ('text'/'json') and research's emit ('compact'/'html'); model-invented enum values; uppercase variants; legacy docs or examples referencing an emit mode that was removed/renamed.
Related errors
- format must be 'text' or 'json', 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/97f27295ad199213.
Report an issue: GitHub.