netchx/netch · error · MessageException
{Name} 控制器启动超时
Error message
{Name} 控制器启动超时 What it means
Thrown by Guard.StartGuardAsync when the startup-watch loop (1000 iterations x Task.Delay(50) = ~50 seconds) elapsed without the child printing either a StartedKeyword or a FailedKeyword, and the process did not exit. State stayed Starting the whole time. This is a timeout, distinct from error 2 which is an explicit failure.
Source
Thrown at Netch/Controllers/Guard.cs:101
// wait ReadOutput change State
for (var i = 0; i < 1000; i++)
{
await Task.Delay(50);
switch (State)
{
case State.Started:
OnStarted();
return;
case State.Stopped:
await StopGuardAsync();
OnStartFailed();
throw new MessageException($"{Name} 控制器启动失败");
}
}
await StopGuardAsync();
throw new MessageException($"{Name} 控制器启动超时");
}
}
private async Task ReadOutputAsync(TextReader reader)
{
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{
await _logStreamWriter!.WriteLineAsync(line);
OnReadNewLine(line);
if (State == State.Starting)
{
if (StartedKeywords.Any(s => line.Contains(s)))
State = State.Started;
else if (FailedKeywords.Any(s => line.Contains(s)))
{
OnStartFailed();View on GitHub (pinned to 9d99eb1c5a)
Solutions
- Inspect logging\<Name>.log to see how far the child got and what it last printed.
- Update the controller's StartedKeywords to match the installed binary's actual ready line.
- Rule out environmental slowdown (antivirus, disk) by launching the binary manually with the same args.
- If startup is genuinely slow, the 1000x50ms budget in StartGuardAsync is the knob (requires code change).
Example fix
// before
protected override IEnumerable<string> StartedKeywords { get; } = new[] { "started" };
// after (match the real banner the newer binary emits)
protected override IEnumerable<string> StartedKeywords { get; } = new[] { "started", "running" }; Defensive patterns
Strategy: try-catch
Try / catch
try { await guard.StartGuardAsync(args); }
catch (MessageException ex) when (ex.Message.EndsWith("控制器启动超时"))
{
Log.Warning("{Name} startup timed out; last log line: {Line}", guard.Name,
File.ReadLines(guard.LogPath).LastOrDefault());
// offer a longer retry or a manual launch with the same args
} Prevention
- Match StartedKeywords exactly to the binary's real ready banner (check its log).
- Manually launch the binary with the controller's assembled arguments to characterize slow startup.
- Disable heavy antivirus real-time scanning of the bin folder during startup.
- Treat a 50-second hang as a keyword-mismatch bug first, an environment problem second.
When it happens
Trigger: Child process starts but hangs before emitting its ready keyword (waiting on network, TUN adapter, slow handshake); StartedKeywords on the Guard subclass don't match what this binary version prints; RedirectOutput=false path is not taken so the loop waits on output that never comes; child blocked on interactive input.
Common situations: Bundled binary upgraded to a version with a different startup banner string, so StartedKeywords no longer matches; slow disk or heavy antivirus scanning delaying child startup past 50s; child waiting on a missing adapter/driver to appear.
Related errors
- {Name} 控制器启动失败
- Unhandled Exception {e.Message}
- AioDNS start failed.
- bin\{mainFile} file not found!
- Lookup Server hostname failed
AI-assisted analysis of netchx/netch@9d99eb1c5a (2026-08-13).
Data as JSON: /api/errors/38c0aa73a0756575.
Report an issue: GitHub.