vxcontrol/pentagi · error

failed to unmarshal search arguments: %w

Error message

failed to unmarshal search arguments: %w

What it means

The graphiti_search tool receives its arguments as a raw JSON byte slice from the LLM and unmarshals them into GraphitiSearchAction. If the payload is not valid JSON for that struct (malformed JSON, wrong types such as a string where an integer array is expected, or a truncated payload), the handler logs the error and returns "failed to unmarshal search arguments: %w". The tool never executes against partially parsed arguments.

Source

Thrown at backend/pkg/tools/graphiti_search.go:160

func (t *graphitiSearchTool) IsAvailable() bool {
	return t.graphitiClient != nil && t.graphitiClient.IsEnabled()
}

// Handle executes the search based on search_type
func (t *graphitiSearchTool) Handle(ctx context.Context, name string, args json.RawMessage) (string, error) {
	if !t.IsAvailable() {
		return "Graphiti knowledge graph is not enabled. No historical context or memory data is available for this search.", nil
	}

	logger := logrus.WithContext(ctx).WithFields(enrichLogrusFields(t.flowID, t.taskID, t.subtaskID, logrus.Fields{
		"tool": name,
		"args": string(args),
	}))

	var searchArgs GraphitiSearchAction
	if err := json.Unmarshal(args, &searchArgs); err != nil {
		logger.WithError(err).Error("failed to unmarshal search arguments")
		return "", fmt.Errorf("failed to unmarshal search arguments: %w", err)
	}

	searchArgs.Query = strings.TrimSpace(searchArgs.Query)

	if searchArgs.Query == "" {
		logger.Error("query parameter is required")
		return "", fmt.Errorf("query parameter is required")
	}
	if searchArgs.SearchType == "" {
		logger.Error("search_type parameter is required")
		return "", fmt.Errorf("search_type parameter is required")
	}

	ctx, observation := obs.Observer.NewObservation(ctx)

	retrieverTitle, ok := graphitiRetrieverTitles[searchArgs.SearchType.String()]
	if !ok {
		retrieverTitle = "retrieve context from graphiti knowledge graph"

View on GitHub (pinned to ea665308ba)

Solutions

  1. Inspect the wrapped json.Unmarshal cause and the logged raw "args" payload to find the exact syntax/type mistake.
  2. Re-issue the tool call as a proper JSON object matching GraphitiSearchAction (search_type, query, message as strings; max_results, max_depth, min_mentions as numbers; node_labels, edge_types as arrays).
  3. If the provider double-encodes arguments (a quoted JSON string), fix the client/adapter so arguments are passed as an object, not a string.
  4. Ensure search_type uses one of the enum values (temporal_window, entity_relationships, diverse_results, episode_context, successful_tools, recent_context, entity_by_label).

Example fix

// before (invalid: numbers as strings, array as string)
{"search_type": "entity_by_label", "query": "find hosts", "max_results": "5", "node_labels": "Host"}
// after
{"search_type": "entity_by_label", "query": "find hosts", "max_results": 5, "node_labels": ["Host"]}
Defensive patterns

Strategy: validation

Validate before calling

func validGraphitiArgs(raw string) error {
    var probe map[string]any
    if err := json.Unmarshal([]byte(raw), &probe); err != nil {
        return err // not valid JSON at all
    }
    for _, intField := range []string{"max_results", "max_depth", "min_mentions"} {
        if v, ok := probe[intField]; ok {
            if _, isNum := v.(float64); !isNum {
                return fmt.Errorf("%s must be a number", intField)
            }
        }
    }
    for _, arrField := range []string{"node_labels", "edge_types"} {
        if v, ok := probe[arrField]; ok {
            if _, isArr := v.([]any); !isArr {
                return fmt.Errorf("%s must be an array", arrField)
            }
        }
    }
    return nil
}

Type guard

func isGraphitiSearchAction(v any) (*GraphitiSearchAction, bool) {
    a, ok := v.(GraphitiSearchAction)
    return &a, ok
}

Try / catch

if _, err := graphitiSearchHandle(ctx, args); err != nil {
    if strings.HasPrefix(err.Error(), "failed to unmarshal search arguments:") {
        // log raw args, fix JSON syntax/types, re-issue the tool call
    }
}

Prevention

When it happens

Trigger: The model emits graphiti_search arguments that are not valid JSON: unquoted keys, trailing commas, single quotes, a JSON-encoded string instead of an object (double-encoded), max_results/max_depth/min_mentions given as strings ("5" instead of 5), or node_labels/edge_types given as a string instead of an array.

Common situations: Weak LLM providers that emit sloppy JSON; prompt frameworks that double-serialize arguments; a provider streaming a truncated tool-call payload; custom OpenAI-compatible endpoints that mangle tool_call.function.arguments.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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