siyuan-note/siyuan · warning
validation did not start within
Error message
validation did not start within %s
What it means
validateResolved gates schema validation behind a semaphore of toolValidationConcurrency=4 slots and a 2-second (toolValidationTime) acquisition deadline. If no slot frees up within 2 seconds — because four other validations are concurrently holding slots on slow, pathological payloads — the validation is abandoned with this timeout rather than queued indefinitely.
Solutions
- Retry the tool call; the condition is usually transient under load.
- Reduce concurrent tool invocations from the client (serialize or batch fewer calls).
- Shrink argument/output payload sizes so each validation completes faster and slots free quickly.
- Increase toolValidationConcurrency or toolValidationTime in validation.go if sustained parallelism is a legitimate workload.
Example fix
// before // client fires 32 tool calls in parallel; 5th+ wait > 2s await Promise.all(ids.map(callTool)) // after // limit in-flight calls to below the 4-slot validation concurrency await pLimit(4, ids.map(callTool))
Defensive patterns
Strategy: retry
Try / catch
err := validator.ValidateInputContext(ctx, args)
if errors.Is(err, context.DeadlineExceeded) || strings.Contains(err.Error(), "validation did not start within") {
// transient slot starvation: back off and retry once
time.Sleep(250 * time.Millisecond)
err = validator.ValidateInputContext(ctx, args)
} Prevention
- Limit concurrent in-flight tool calls to roughly the validation concurrency (4).
- Keep argument payloads small so validations complete quickly and free slots.
- Add client-side backoff/retry on this timeout — it is transient under load.
- Monitor validation latency; persistently hitting the 2s budget means lowering parallelism or raising limits.
When it happens
Trigger: More than 4 concurrent tool calls whose input/output validation is in progress, each taking long enough (large payloads near the 8 MiB / 262144-node limits) that a fifth waits over 2 seconds for a slot; typically under heavy parallel MCP traffic.
Common situations: Load/stress tests firing many simultaneous tool calls; a slow or adversarial payload monopolizing validation slots; saturated CPU on the host slowing jsonschema.Validate for all in-flight calls.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- validation exceeded
- attr must be a string or null (got %T)
- connect
- each key must be an object
- each key requires name and type
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/b6228b0e38013ce7.
Report an issue: GitHub.
Appendix: 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 9f775e8a12)