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
- Reduce concurrency of tool invocations or batch them so fewer validations run simultaneously.
- Simplify the schemas (fewer/cheaper regex patterns, shallower $ref chains) so each schema.Validate completes faster and frees its slot.
- Pass a context with a longer deadline if the caller can tolerate waiting, and ensure the caller does not cancel prematurely.
- 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
- Bound the concurrency of tool calls you issue in parallel to leave validation slots free.
- Simplify schemas so each validation finishes quickly and frees its slot.
- Pass a context deadline sized to tolerate the 2s start window under load.
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
- validation exceeded %s
- tools/list returned an empty response
- tools/list repeated cursor %q
- unsupported server type: %s
- command is required for stdio server
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/b6228b0e38013ce7.
Report an issue: GitHub.