siyuan-note/siyuan · warning

validation did not start within %s

Error message

validation did not start within %s

What it means

Thrown by validateResolved when the bounded validation semaphore (validationSlots, capacity toolValidationConcurrency=4) could not be acquired within toolValidationTime (2s). All 4 concurrent validation slots were occupied for the full window, so this validation could not even start. It is a back-pressure/fairness guard, not a verdict on the value being validated.

Source

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

	if err = validateJSONComplexity(canonical, maxToolValueDepth, maxToolValueNodes); err != nil {
		return nil, err
	}
	return canonical, nil
}

func validateResolved(ctx context.Context, validationSlots chan struct{}, schema *jsonschema.Resolved, value any) error {
	if ctx == nil {
		ctx = context.Background()
	}
	timer := time.NewTimer(toolValidationTime)
	defer timer.Stop()

	select {
	case validationSlots <- struct{}{}:
	case <-ctx.Done():
		return ctx.Err()
	case <-timer.C:
		return fmt.Errorf("validation did not start within %s", toolValidationTime)
	}

	result := make(chan error, 1)
	go func() {
		err := schema.Validate(value)
		<-validationSlots
		result <- err
	}()

	select {
	case err := <-result:
		return err
	case <-ctx.Done():
		return ctx.Err()
	case <-timer.C:
		return fmt.Errorf("validation exceeded %s", toolValidationTime)
	}
}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Reduce concurrency of tool invocations or batch them so fewer validations run simultaneously.
  2. Simplify the schemas (fewer/cheaper regex patterns, shallower $ref chains) so each schema.Validate completes faster and frees its slot.
  3. Pass a context with a longer deadline if the caller can tolerate waiting, and ensure the caller does not cancel prematurely.
  4. If sustained load is expected, consider whether per-tool pre-compilation can eliminate runtime validation entirely.

Example fix

// before: fire 50 tool calls concurrently, saturating the 4-slot pool
for _, t := range tools { go call(t) }
// after: bound concurrency to leave headroom
sem := make(chan struct{}, 4)
for _, t := range tools { sem <- struct{}{}; go func(t){ defer func(){<-sem}(); call(t) }(t) }
Defensive patterns

Strategy: retry

Try / catch

err := validator.ValidateInputContext(ctx, args)
if err != nil && strings.Contains(err.Error(), "validation did not start") {
    // back off and retry once; otherwise propagate
}

Prevention

When it happens

Trigger: Four other schema.Validate calls are already running (each potentially slow) and a fifth validation waits the full 2s without obtaining a slot; the context has not been cancelled in the meantime.

Common situations: A burst of many tool calls under load (e.g., an agent fan-out) saturates the 4-slot pool; one or more schemas are pathologically slow to validate (deep $ref graphs, expensive patterns) and hog slots; running on a CPU-throttled host where validation is slow.

Related errors


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