{"id":"83900b20b7e03027","repo":"gohugoio/hugo","slug":"errshutdown","errorCode":"ErrShutdown","errorMessage":"dispatcher is shutting down","messagePattern":"dispatcher is shutting down","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/warpc/warpc.go","lineNumber":201,"sourceCode":"\tstdout       io.WriteCloser\n\tstdoutBinary hugio.ReadWriteCloser\n\tdec          *json.Decoder\n\tenc          *json.Encoder\n}\n\nfunc (p *inOut) Close() error {\n\tif err := p.stdin.Close(); err != nil {\n\t\treturn err\n\t}\n\n\t// This will also close the underlying writers.\n\tif err := p.stdout.Close(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nvar ErrShutdown = fmt.Errorf(\"dispatcher is shutting down\")\n\nvar timerPool = sync.Pool{}\n\nfunc getTimer(d time.Duration) *time.Timer {\n\tif v := timerPool.Get(); v != nil {\n\t\ttimer := v.(*time.Timer)\n\t\ttimer.Reset(d)\n\t\treturn timer\n\t}\n\treturn time.NewTimer(d)\n}\n\nfunc putTimer(t *time.Timer) {\n\tif !t.Stop() {\n\t\tselect {\n\t\tcase <-t.C:\n\t\tdefault:\n\t\t}","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/internal/warpc/warpc.go#L183-L219","documentation":"ErrShutdown (internal/warpc/warpc.go:201) is returned by the warpc dispatcher pool when a call is attempted or in flight while the pool of WASM worker processes (used for katex, AVIF, WebP, etc.) is being closed. It signals that the RPC layer is tearing down, not that the request itself was invalid.","triggerScenarios":"dispatcherPool.Execute or send() racing with pool Close() — e.g. image/katex rendering still running while Hugo shuts down, a fatal error in one dispatcher triggering pool teardown, or a server rebuild closing the pool mid-render.","commonSituations":"Ctrl-C or build abort while templates are still rendering math/images; a preceding WASM module crash (look for earlier errors in the log — this one is often the secondary symptom); very long builds where an earlier fatal dispatcher error closed the pool.","solutions":["Scroll up in the log for the first error — an earlier WASM module failure usually closed the pool; fix that root cause.","If it only occurs on interrupt/shutdown, it's benign; ignore it.","Re-run the build; if it reproduces cleanly, upgrade Hugo and report the sequence of errors."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isShutdown(err error) bool { return errors.Is(err, warpc.ErrShutdown) }","tryCatchPattern":"res, err := d.Execute(ctx, msg)\nif errors.Is(err, warpc.ErrShutdown) {\n    // dispatcher closed (build teardown) — stop submitting work, do not retry\n    return err\n}","preventionTips":["Check errors.Is(err, ErrShutdown) and treat it as a signal to stop, not a transient failure","Order teardown so no goroutine submits calls after Close()","Tie call submission to the same context that governs dispatcher lifetime"],"tags":["hugo","warpc","wasm","shutdown","lifecycle","concurrency"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}