What happened
During netclaw setup (Step 10: Health Check), the daemon crashed with an unhandled exception from ExposureModeValidationService.StartAsync when tailscaled was not detected as a running process:
Mode='tailscale-funnel' requires 'tailscaled' to be running. Start 'tailscaled' or set ExposureMode to 'local' in netclaw.json.
at Netclaw.Daemon.Services.ExposureModeValidationService.StartAsync(CancellationToken cancellationToken)
at Microsoft.Extensions.Hosting.Internal.Host.ForeachService[T](...)
at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
at Program.<Main>$(String[] args)
This resulted in the health check failing with "Daemon did not become ready (personality setup skipped)" and a crash log written — but the error was presented as a raw stack dump rather than a graceful setup failure message.
Expected behavior
-
Graceful error handling — The exception from ExposureModeValidationService should be caught by the setup/health check flow and presented as a clean, user-facing error message. The daemon should not emit a full stack trace during setup.
-
Relaxed validation for sidecar/container deployments — The current check uses Process.GetProcessesByName("tailscaled") to verify the tunnel process is running locally. This is too strict for deployments where tailscale funnel runs in a sidecar container or on a different host. The check should either:
- Attempt a connectivity test against the funnel endpoint instead of checking for a local process, OR
- Provide a configuration option to skip the process check (e.g.,
ExposureModeValidationSkip or a tailscale-funnel mode that trusts the tunnel is externally managed)
Steps to reproduce
- Configure
ExposureMode to tailscale-funnel in netclaw.json
- Ensure
tailscaled is not running locally (e.g., running in a sidecar container or not installed)
- Run
netclaw setup
- Observe the daemon crash with a raw stack trace at Step 10
Environment
- Component:
Netclaw.Daemon.Services.ExposureModeValidationService
- File:
src/Netclaw.Daemon/Services/ExposureModeValidationService.cs
- Method:
StartAsync — throws InvalidOperationException which is not caught by the hosting layer during setup
What happened
During
netclaw setup(Step 10: Health Check), the daemon crashed with an unhandled exception fromExposureModeValidationService.StartAsyncwhentailscaledwas not detected as a running process:This resulted in the health check failing with "Daemon did not become ready (personality setup skipped)" and a crash log written — but the error was presented as a raw stack dump rather than a graceful setup failure message.
Expected behavior
Graceful error handling — The exception from
ExposureModeValidationServiceshould be caught by the setup/health check flow and presented as a clean, user-facing error message. The daemon should not emit a full stack trace during setup.Relaxed validation for sidecar/container deployments — The current check uses
Process.GetProcessesByName("tailscaled")to verify the tunnel process is running locally. This is too strict for deployments wheretailscale funnelruns in a sidecar container or on a different host. The check should either:ExposureModeValidationSkipor atailscale-funnelmode that trusts the tunnel is externally managed)Steps to reproduce
ExposureModetotailscale-funnelinnetclaw.jsontailscaledis not running locally (e.g., running in a sidecar container or not installed)netclaw setupEnvironment
Netclaw.Daemon.Services.ExposureModeValidationServicesrc/Netclaw.Daemon/Services/ExposureModeValidationService.csStartAsync— throwsInvalidOperationExceptionwhich is not caught by the hosting layer during setup