iOfficeAI/OfficeCLI · error · InvalidOperationException

Another watch process is already running{url} for {_filePath

Error message

Another watch process is already running{url} for {_filePath}

What it means

Thrown by WatchServer.RunAsync when a watch is already running for the same file (GetExistingWatchPort returns a port from the marker file). It prevents two watch processes racing on one file. The message includes the existing URL when the port is positive, so the user can reuse the running watch instead of starting a duplicate.

Source

Thrown at src/officecli/Core/Watch/WatchServer.cs:316

    private void DeleteMarker()
    {
        try
        {
            var markerPath = GetWatchMarkerPath(_filePath);
            if (File.Exists(markerPath)) File.Delete(markerPath);
        }
        catch { /* best-effort cleanup */ }
    }

    public async Task RunAsync(CancellationToken externalToken = default)
    {
        // Prevent duplicate watch processes for the same file
        var existingPort = GetExistingWatchPort(_filePath);
        if (existingPort.HasValue)
        {
            var url = existingPort.Value > 0 ? $" at http://localhost:{existingPort.Value}" : "";
            throw new InvalidOperationException($"Another watch process is already running{url} for {_filePath}");
        }

        using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token, externalToken);
        var token = linkedCts.Token;

        _tcpListener.Start();
        // --port 0 asks the OS for an ephemeral port; resolve the real one
        // before it reaches the marker file and the printed URL, otherwise
        // both would say 0 and per-file discovery (mark/goto/unwatch,
        // IsWatching) breaks.
        if (_port == 0)
            _port = ((IPEndPoint)_tcpListener.LocalEndpoint!).Port;
        WriteMarker();
        Console.WriteLine($"Watch: http://localhost:{_port}");
        Console.WriteLine($"Watching: {_filePath}");
        Console.WriteLine("Press Ctrl+C to stop.");

        // Hook graceful shutdown signals. Cooperatively terminating a

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Reuse the existing watch — open/connect to the URL printed in the message.
  2. Stop the running watch first (unwatch / SendClose / Ctrl+C), then start a new one.
  3. If the marker is stale (no real watch bound), remove the marker file and retry.

Example fix

# before: second `watch file.docx` while one runs
# after: reuse the running watch
#   open the URL from the error, or stop it first:
unwatch file.docx   # then re-run watch
Defensive patterns

Strategy: validation

Validate before calling

// Check before starting a watch
int? port = WatchServer.GetExistingWatchPort(filePath);
if (port.HasValue)
    Console.WriteLine($"already watching at http://localhost:{port.Value}");

Try / catch

try { await server.RunAsync(token); }
catch (InvalidOperationException ex) when (ex.Message.Contains("already running"))
{ /* reuse the existing watch URL, or SendClose then retry */ }

Prevention

When it happens

Trigger: Starting a second watch for a file that already has a live watch process; a previous watch left a stale marker file pointing at a still-bound port.

Common situations: Forgetting a watch is already running; a stuck/stale marker after an unclean shutdown; automation spawning parallel watches for the same file.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/5e231d4bb4ca43ba. Report an issue: GitHub.