charmbracelet/crush · error

questions must be an array: %w

Error message

questions must be an array: %w

What it means

The question tool accepts its questions field either as a real JSON array or as a string containing a JSON array. When the string-fallback path is taken, the trimmed string must itself parse into a question array; if json.Unmarshal into []QuestionItem fails, this error wraps the parse failure, meaning the input shape is neither a plain array nor a string-encoded array.

Source

Thrown at internal/agent/tools/question.go:51

		*Alias
	}{
		Alias: (*Alias)(p),
	}
	if err := json.Unmarshal(data, aux); err != nil {
		return err
	}
	if len(aux.Questions) == 0 {
		return nil
	}
	// Try array first.
	if err := json.Unmarshal(aux.Questions, &p.Questions); err != nil {
		// Fall back to string-encoded JSON array.
		var s string
		if err2 := json.Unmarshal(aux.Questions, &s); err2 != nil {
			return err
		}
		if err2 := json.Unmarshal([]byte(strings.TrimSpace(s)), &p.Questions); err2 != nil {
			return fmt.Errorf("questions must be an array: %w", err2)
		}
	}
	return nil
}

// QuestionItem is a single question from the tool input.
type QuestionItem struct {
	Label       string           `json:"label,omitempty" description:"Short tab header label (3 words max)."`
	Type        string           `json:"type" description:"The type of question: yes_no, single_choice, multi_choice, or free_text"`
	Question    string           `json:"question" description:"The question text"`
	Description string           `json:"description" description:"Required markdown description shown below the question"`
	Choices     []QuestionChoice `json:"choices,omitempty" description:"List of choices"`
	Options     []QuestionChoice `json:"options,omitempty"` // alias for Choices
}

// GetChoices returns choices, preferring the Choices field over Options.
func (q QuestionItem) GetChoices() []QuestionChoice {
	if len(q.Choices) > 0 {

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Send questions as a proper JSON array of question objects, not a string
  2. If double-encoded, decode the string once and confirm it is valid JSON array syntax
  3. Log the raw aux.Questions value to see the exact malformed payload
  4. Upgrade/fix the client or prompt that produces stringified arguments

Example fix

// before
{"questions": "[{question: 'title?'}]"} // invalid inner JSON
// after
{"questions": [{"question": "title?", "options": ["a","b"]}]}
Defensive patterns

Strategy: validation

Validate before calling

switch v := raw.(type) {
case []any:
    // ok: already an array
case string:
    var check []any
    if err := json.Unmarshal([]byte(strings.TrimSpace(v)), &check); err != nil {
        return fmt.Errorf("questions is not a JSON array: %w", err)
    }
default:
    return fmt.Errorf("questions must be array or string-encoded array")
}

Type guard

func isQuestionArray(raw json.RawMessage) bool {
    var arr []QuestionItem
    return json.Unmarshal(raw, &arr) == nil
}

Try / catch

if err := json.Unmarshal(data, &params); err != nil {
    var syn *json.SyntaxError
    if errors.As(err, &syn) {
        // log syn.Offset to locate malformed inner JSON
    }
}

Prevention

When it happens

Trigger: Client sends questions as a string whose content is not a valid JSON array (e.g. plain prose, single-quoted pseudo-JSON, or a JSON object) — or the field is some other type that failed the primary array decode.

Common situations: Models emitting double-encoded or malformed JSON in tool arguments; middleware re-serializing arguments to strings; hand-written test payloads with unquoted JSON.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/8db9658a683fb06f. Report an issue: GitHub.