vxcontrol/pentagi · error

failed to unmarshal %s search answer action arguments: %w

Error message

failed to unmarshal %s search answer action arguments: %w

What it means

The `search answer` tool received arguments that failed to decode into `SearchAnswerAction`. As with the memory tool, the raw LLM-emitted JSON did not match the expected struct (fields, types, or overall JSON validity), and the underlying decode error is wrapped with the tool name.

Source

Thrown at backend/pkg/tools/search.go:89

func (s *search) Handle(ctx context.Context, name string, args json.RawMessage) (string, error) {
	ctx, observation := obs.Observer.NewObservation(ctx)
	logger := logrus.WithContext(ctx).WithFields(enrichLogrusFields(s.flowID, s.taskID, s.subtaskID, logrus.Fields{
		"tool": name,
		"args": string(args),
	}))

	if s.store == nil {
		logger.Error("pgvector store is not initialized")
		return "", fmt.Errorf("pgvector store is not initialized")
	}

	switch name {
	case SearchAnswerToolName:
		var action SearchAnswerAction
		if err := json.Unmarshal(args, &action); err != nil {
			logger.WithError(err).Error("failed to unmarshal search answer action arguments")
			return "", fmt.Errorf("failed to unmarshal %s search answer action arguments: %w", name, err)
		}

		filters := map[string]any{
			"doc_type":    searchVectorStoreDefaultType,
			"answer_type": action.Type,
		}

		metadata := langfuse.Metadata{
			"tool_name":     name,
			"message":       action.Message,
			"limit":         searchVectorStoreResultLimit,
			"threshold":     searchVectorStoreThreshold,
			"doc_type":      searchVectorStoreDefaultType,
			"answer_type":   action.Type,
			"queries_count": len(action.Questions),
		}

		retriever := observation.Retriever(

View on GitHub (pinned to ea665308ba)

Solutions

  1. Read the wrapped `%w` error to find the exact field/type mismatch.
  2. Diff the tool JSON schema sent to the model against `SearchAnswerAction`.
  3. Inspect the raw args logged with the error for truncation or markdown fences.
  4. Retry, or constrain the model to JSON mode / a stronger provider if it recurs.

Example fix

// before
args := []byte(`{"questions":["q1"],"answer_type":3}`) // answer_type must be a string
// after
args, _ := json.Marshal(SearchAnswerAction{Questions: []string{"q1"}, Type: "summary"})
Defensive patterns

Strategy: validation

Validate before calling

var probe struct {
    Questions []any  `json:"questions"`
    Type      string `json:"answer_type"`
}
if err := json.Unmarshal(args, &probe); err != nil {
    return fmt.Errorf("invalid search answer args: %w", err)
}
if len(probe.Questions) == 0 || probe.Type == "" {
    return errors.New("questions and answer_type are required")
}

Type guard

func isValidSearchAnswerArgs(args []byte) bool {
    var a search.SearchAnswerAction
    return json.Unmarshal(args, &a) == nil && len(a.Questions) > 0 && a.Type != ""
}

Try / catch

out, err := searchTool.Handle(ctx, tools.SearchAnswerToolName, args)
if err != nil {
    if errors.Is(err, io.ErrUnexpectedEOF) || strings.Contains(err.Error(), "cannot unmarshal") {
        return retryWithSchemaReminder(ctx, args)
    }
    return err
}

Prevention

When it happens

Trigger: Agent calls the `search answer` tool and `json.Unmarshal(args, &SearchAnswerAction{})` fails — e.g. `questions` not an array of strings, `answer_type` wrong type, truncated or fenced JSON from the model.

Common situations: Model emitting the schema of a similar tool (`store answer`) instead; outdated tool description in the prompt; providers with weaker JSON-mode guarantees (local Ollama models); action struct changed without updating the advertised schema.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/504d045a099e3b3f. Report an issue: GitHub.