Skip to content

fix: open Windows file URLs that name a drive letter - #1592

Open
sushantlokhande14 wants to merge 1 commit into
uber-go:masterfrom
sushantlokhande14:fix/windows-file-url-sink
Open

sushantlokhande14 wants to merge 1 commit into
uber-go:masterfrom
sushantlokhande14:fix/windows-file-url-sink

Conversation

@sushantlokhande14

@sushantlokhande14 sushantlokhande14 commented Sep 12, 2026

Copy link
Copy Markdown

Fixes #621.

Problem

RFC 8089 puts a Windows drive letter after a leading slash, so file:///C:/logs/app.log parses to the path /C:/logs/app.log. newFileSinkFromURL passed that straight to os.OpenFile, and Windows cannot open it.

Reproduced on Windows 11, Go 1.26.5, against master. The same file, named three ways:

C:...Temp...plain.log                          err=<nil>
file:///C:/Users/.../Temp/url.log              err=open sink: open /C:/Users/.../Temp/url.log:
                                                   The filename, directory name, or volume label
                                                   syntax is incorrect.
file://localhost/C:/Users/.../Temp/url.log     err=same

A plain absolute path works, because #1159 routes those through filepath.IsAbs. Every file URL naming a local drive fails, with or without an explicit localhost host. That is the gap #1159 called out in its own description: "this change does not bring full Windows support ... Windows file URIs don't work."

Fix

On Windows, drop the slash ahead of a drive letter before opening. Windows accepts the forward slashes that remain, so no other translation is needed — verified directly with os.OpenFile on a C:/... path.

Whether to apply the rule is a field on sinkRegistry, defaulting to runtime.GOOS == "windows", rather than a runtime.GOOS check at the call site. That is deliberate: on #621 @abhinav noted the issue stayed open partly because "none of the active maintainers have Windows machines" and the edit-test-debug loop was too slow. CI here is Ubuntu-only and sink_windows_test.go never runs in it. Making this a registry field means the Windows behaviour is exercised on every platform, in CI, through the openFile stub #1159 already added.

The prefix test is deliberately strict: a slash, an ASCII letter, a colon, then a slash. Anything shorter or otherwise shaped is returned unchanged.

Tests

TestFileURLPath covers the helper directly (8 cases, both platform modes): drive letters upper and lower, a path with no drive, a bare /C:, a non-letter "drive", empty input, and non-Windows inputs that must keep the leading slash.

TestWindowsFileURLs drives the real path through newSink with isWindows forced on and openFile stubbed, asserting the exact filename opened — empty host, explicit localhost, and a percent-encoded space. Both run on Linux.

Reverting only the call site (sr.fileURLPath(u.Path) back to u.Path) makes all three TestWindowsFileURLs cases fail, so the test guards the fix rather than the helper.

Test construction on Windows

TestOpen and TestOpenOtherErrors built file URLs by appending the temp path to "file://". On Unix that yields file:///tmp/... because the path already starts with a slash. On Windows it puts the drive in the host position, producing an unparseable URL, which is why those tests fail there today. They now build the RFC 8089 form. The output is byte-identical on Unix-like systems, and TestOpen passes on Windows as a result.

Verification

  • go test . on Windows: TestOpen, TestOpenOtherErrors, TestFileURLPath, TestWindowsFileURLs and the existing TestWindowsPaths all pass. TestOpen and TestOpenOtherErrors failed before this change.
  • Remaining Windows failures are pre-existing and unrelated. I confirmed each fails identically on unmodified master in a separate worktree: TestConfig, TestConfigWithSamplingHook and zapcore.TestIOCore (temp-file cleanup while a log file is still open), TestStacktraceFiltersVendorZap, and Example_basicConfiguration.
  • gofmt and gofumpt clean on all three files; golangci-lint run ./... reports nothing for them.

Relationship to #624

#624 (2018) proposed the same basic idea and is still open, but has not been touched in years. This supersedes it rather than duplicating it:

  • it indexes u.Path[2] with no length check, so a URL like file:///a panics
  • it does not check that the drive character is a letter
  • it has no tests, and on Ubuntu CI there was no way to run any
  • it patches newFileSink, which Open absolute paths as files, limited Windows support #1159 replaced in 2022

Its review also stalled on whether the change was needed at all, with two slashes suggested instead. #621 settled that two days later: "It looks like it has to be 3 slashes."

Note on #1398

#1398 adds a .. check immediately above the line this touches. If that lands first, this becomes sr.newFileSinkFromPath(sr.fileURLPath(u.Path)) after that check — happy to rebase in whichever order suits you.

Non-goals

UNC URLs (file://server/share/...) are still rejected by the existing host check. A bare file:///C: with no trailing slash is left unchanged, since it names a drive rather than a file. The temp-file cleanup failures above are a separate issue.

AI tool usage

AI assistance (Claude Code) was used to locate this defect and draft the patch and tests. I reviewed every line, ran everything above locally on Windows, and can defend the change end to end.

A file URL for a Windows path puts the drive letter after a leading slash, as
RFC 8089 requires: file:///C:/logs/app.log parses to the path
"/C:/logs/app.log". newFileSinkFromURL passed that path straight to
os.OpenFile, which Windows rejects ("The filename, directory name, or volume
label syntax is incorrect"), so every file URL naming a local drive failed,
with or without an explicit localhost host.

On Windows, drop the slash ahead of a drive letter before opening the file.
Windows accepts the forward slashes that remain, so no further translation is
needed. Whether to apply the rule is a field on the sink registry, defaulting
to runtime.GOOS, so the Windows handling is exercised on every platform
through the existing openFile stub rather than only on Windows machines.

The file URL cases in TestOpen and TestOpenOtherErrors built URLs by appending
the temp path to "file://", which produces "file://C:\..." on Windows. They now
build the RFC 8089 form, which is byte-identical on Unix-like systems.

Refs uber-go#621
@CLAassistant

CLAassistant commented Sep 12, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@codecov

codecov Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.88%. Comparing base (bb1a55d) to head (a916ac0).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1592      +/-   ##
==========================================
+ Coverage   98.85%   98.88%   +0.03%     
==========================================
  Files          53       53              
  Lines        3047     3057      +10     
==========================================
+ Hits         3012     3023      +11     
+ Misses         35       34       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Windows paths not supported by default file sink

2 participants