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

  1. Use emit: "compact" (default, inline synthesis) or emit: "html" (also saves a shareable HTML brief) — or omit emit entirely.
  2. Don't pass 'json' or 'text' to research; those belong to the preflight tool's format argument.
  3. Check casing — the comparison is case-sensitive and exact.
  4. 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

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


AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15). Data as JSON: /api/errors/97f27295ad199213. Report an issue: GitHub.