Skip to content

fix(desktop): match WSAEADDRINUSE on Windows and repair the Go test build - #1909

Open
JasonBuildAI wants to merge 1 commit into
TencentCloud:developfrom
JasonBuildAI:fix/agent-c1f7-work
Open

JasonBuildAI wants to merge 1 commit into
TencentCloud:developfrom
JasonBuildAI:fix/agent-c1f7-work

Conversation

@JasonBuildAI

@JasonBuildAI JasonBuildAI commented Oct 11, 2026 •

Copy link
Copy Markdown

Summary

desktop/src's Go test package does not compile on develop (filedrop_test.go:91 redeclares err), and that compile error hides a real Windows-only defect from #1874: listenProbe matches busy ports with errors.Is(err, syscall.EADDRINUSE), but Windows reports a busy socket as Winsock errno 10048 (WSAEADDRINUSE) while Go's syscall.EADDRINUSE on Windows is the synthetic value 0x20000002, so the check can never be true. A busy preferred port (default 8088) therefore aborts firstFreePort instead of skipping ahead, and the desktop stops with a raw Winsock message instead of starting on 8089+ - the exact "second Octop instance" scenario #1874 set out to fix. Fixes #1905.

Target branch

  • Base is develop (feature / fix - default)
  • Base is main (release/* or hotfix/* only)

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Refactor / chore
  • Release / hotfix

How it is fixed

  • desktop/src/filedrop_test.go: drop the duplicate var err error in TestDropServerServesOnlyPublishedFiles, so go test ./... / go vet ./... build again (go build ./... was never affected because it ignores _test.go files).
  • New addrInUse(err error) bool helper behind build tags: process_windows.go uses errors.Is(err, windows.WSAEADDRINUSE) (golang.org/x/sys/windows, already a direct dependency), process_unix.go keeps syscall.EADDRINUSE. listenProbe calls the helper.
  • Errors that are not "address in use" (e.g. an unresolvable host) still propagate, so cannotBindError / noFreePortError handling in boot() is unchanged (TestChooseFreePortStopsWhenHostCannotBind still passes).
  • New test TestChooseFreePortSkipsALiveListener binds a real listener and asserts chooseFreePort skips it - the user-visible path that regressed.

Test plan

Windows 11 x64, go1.25.9, worktree at 544ea757 (develop).

  1. Before - pristine develop:
$ cd desktop/src && go test ./...
# octop.desktop [octop.desktop.test]
.\filedrop_test.go:91:6: err redeclared in this block
	.\filedrop_test.go:75:8: other declaration of err
FAIL	octop.desktop [build failed]
?   	octop.desktop/cmd/devserver	[no test files]

$ go vet ./...
vet.exe: .\filedrop_test.go:91:6: err redeclared in this block
  1. Fixing only the duplicate declaration reveals the hidden Windows failure:
--- FAIL: TestListenProbeSeesOpenListener (0.00s)
    process_test.go:138: inUse = false, err = listen tcp 127.0.0.1:51360: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
FAIL	octop.desktop	1.317s

Errno probe on the same host (scratch program, not committed):

busy bind err     = listen tcp 127.0.0.1:56430: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
errors.Is(err, syscall.EADDRINUSE)          = false   // syscall.EADDRINUSE = 0x20000002
errors.Is(err, windows.WSAEADDRINUSE)       = true    // 0x2740 = 10048
  1. Old process.go + the new tests (both fail, so the new test is a real regression guard):
--- FAIL: TestChooseFreePortSkipsALiveListener (0.02s)
    process_test.go:142: chooseFreePort(60857) = listen tcp 127.0.0.1:60857: bind: Only one usage ...
--- FAIL: TestListenProbeSeesOpenListener (0.00s)
    process_test.go:157: inUse = false, err = ...
FAIL	octop.desktop	0.825s
  1. With the fix:
$ gofmt (LF-normalized copies of the five touched files) -> clean
$ go build ./... -> exit 0
$ go vet ./...   -> exit 0
$ go test ./...
ok  	octop.desktop	1.465s
?   	octop.desktop/cmd/devserver	[no test files]

$ go test -v -run 'TestListenProbeSeesOpenListener|TestChooseFreePortSkipsALiveListener|TestChooseFreePortStopsWhenHostCannotBind'
--- PASS: TestChooseFreePortStopsWhenHostCannotBind (0.06s)
--- PASS: TestChooseFreePortSkipsALiveListener (0.00s)
--- PASS: TestListenProbeSeesOpenListener (0.00s)
PASS
ok  	octop.desktop	0.208s

Python side (this change touches no Python code; make is unavailable on Windows, so the Makefile targets were run as their underlying commands):

  • uv run ruff check src tests -> All checks passed!
  • uv run ruff format --check src tests -> 1177 files already formatted
  • uv run mypy src/octop -> Success: no issues found in 569 source files
  • uv run pytest -n auto -m "not live" -q -> 28 failed, 4409 passed, 202 skipped in 26:42
  • Baseline on the same host with the change stashed, same 10 files as the failures above: 27 failed, 67 passed, 1 skipped; the extra tests/unit/connectors/test_agently_auth.py::test_login_error_and_refresh_never_expose_cli_output failure in the full run passes standalone and under -n auto on the pristine tree (test-ordering/resource flake). The 27 environment failures are pre-existing (tests/unit/db/* legacy-migration fixtures and tests/unit/test_vendor_wheels_argv.py, the latter tracked by test(desktop): apply posix_only to the vendor-wheels argv cases #1812).

Checklist

  • Ship-bar commands run locally (see Test plan; make is unavailable in this Windows clone, so the Makefile targets were run as their underlying commands). ruff check / ruff format --check / mypy pass; the pytest -m "not live" run reports 28 failures, all pre-existing on this host (27 reproduced with this change stashed, plus one xdist flake), and this PR touches no Python file.
  • Added/updated tests
  • Updated CHANGELOG.md
  • README / docs updated (not needed)

Note for reviewers, not part of this PR: ci.yml has no Go test job and octop-desktop.yml only packages the app, which is why a non-compiling test package sat unnoticed since #1874. Adding a go vet ./... + go test ./... job for desktop/src would catch this class of regression; happy to follow up separately if wanted.

AI disclosure: this change was drafted with AI assistance at my request, under my direction and review.

…uild

listenProbe matched a busy port with syscall.EADDRINUSE, whose Windows
value (0x20000002) never equals the Winsock errno net returns (10048).
A busy preferred port therefore aborted firstFreePort instead of
skipping ahead, and the desktop stopped on its saved port instead of
trying 8089+. Detect the platform errno via a build-tagged addrInUse
helper (x/sys/windows WSAEADDRINUSE on Windows, EADDRINUSE elsewhere).

filedrop_test.go also declared err twice, so the desktop test package
did not compile and hid the failure above; drop the duplicate and add
a live-listener regression test for the skip path.

Fixes TencentCloud#1905.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant