siyuan-note/siyuan · error

prepare structured content: %w

Error message

prepare structured content: %w

What it means

Wraps any error returned by prepareValidationValue while preparing the structured content of a tool result for output-schema validation. The underlying cause is almost always a JSON-complexity limit (depth/nodes/bytes exceeded, see errors 364/367/368) or a json.Marshal/Unmarshal failure on the StructuredContent value. The %w verb preserves the wrapped error for inspection with errors.Is/errors.As.

Source

Thrown at kernel/mcp/tools/validation.go:132

		return err
	}
	return validateResolved(ctx, validator.validationSlots, validator.input, value)
}

func (validator *ToolValidator) ValidateOutput(result CallToolResult) error {
	return validator.ValidateOutputContext(context.Background(), result)
}

func (validator *ToolValidator) ValidateOutputContext(ctx context.Context, result CallToolResult) error {
	if validator == nil || validator.output == nil || result.IsError {
		return nil
	}
	if !result.HasStructuredContent() {
		return fmt.Errorf("structured content is required when an output schema is defined")
	}
	value, err := prepareValidationValue(result.StructuredContent)
	if err != nil {
		return fmt.Errorf("prepare structured content: %w", err)
	}
	return validateResolved(ctx, validator.validationSlots, validator.output, value)
}

func prepareValidationValue(value any) (any, error) {
	if err := validateJSONComplexity(value, maxToolValueDepth, maxToolValueNodes); err != nil {
		return nil, err
	}
	data, err := json.Marshal(value)
	if err != nil {
		return nil, err
	}
	if len(data) > maxToolValueBytes {
		return nil, fmt.Errorf("value exceeds %d bytes", maxToolValueBytes)
	}
	var canonical any
	if err = json.Unmarshal(data, &canonical); err != nil {
		return nil, err

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Inspect the wrapped error with errors.Unwrap/errors.As to identify whether it is a size, depth, node-count, or marshal error, then address that specific limit.
  2. Ensure StructuredContent is a plain JSON-serializable value (map[string]any, slices of primitives, structs with JSON tags).
  3. Trim or paginate large outputs so the marshaled size stays under 8 MiB and depth under 128.
  4. Unit-test the handler's StructuredContent by marshaling it through encoding/json before wiring it to the tool.

Example fix

// before: handler stores a non-serializable value
result.StructuredContent = someLiveObjectWithChannels
// after
result.StructuredContent = map[string]any{"id": obj.ID, "name": obj.Name}
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := json.Marshal(structuredContent); err != nil { return fmt.Errorf("structured content not serializable: %w", err) }

Try / catch

err := validator.ValidateOutputContext(ctx, result)
if err != nil {
    if inner := errors.Unwrap(err); inner != nil { /* inspect complexity/marshal cause */ }
    // degrade: return text content only, or convert to IsError result
}

Prevention

When it happens

Trigger: ValidateOutputContext calls prepareValidationValue(result.StructuredContent); that value fails validateJSONComplexity, exceeds maxToolValueBytes (8 MiB), or contains a type that json.Marshal cannot encode (e.g., a channel, func, or cyclic struct).

Common situations: A tool returns a deeply nested or very large structured object; a handler stores a non-JSON-serializable Go value (chan, func, unsafe.Pointer, or a struct with unexported fields and no marshaler) into StructuredContent; a handler accidentally embeds the entire request or a live pointer graph.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/1979e25f70b41b5c. Report an issue: GitHub.