golang/go · error
internal error: failed to construct url: %v
Error message
internal error: failed to construct url: %v
What it means
Returned by doPkgsite when url.JoinPath("http://"+addr, urlPath) fails while constructing the documentation URL. The error is explicitly prefixed 'internal error' because, given the preceding pickUnusedPort success and the expected shape of urlPath, JoinPath should not fail — its failure indicates a malformed caller-supplied urlPath (control characters, bad escaping).
Source
Thrown at src/cmd/go/internal/doc/pkgsite.go:112
b.Do(ctx, a)
// Both paths return an executable in GOCACHE: CachedExecutable is set on
// fresh builds, while BuiltTarget is set on cache hits.
if cached := a.CachedExecutable(); cached != "" {
return cached
}
return a.BuiltTarget()
}
func doPkgsite(ctx context.Context, urlPath, fragment string) error {
port, err := pickUnusedPort()
if err != nil {
return fmt.Errorf("failed to find port for documentation server: %v", err)
}
addr := fmt.Sprintf("localhost:%d", port)
path, err := url.JoinPath("http://"+addr, urlPath)
if err != nil {
return fmt.Errorf("internal error: failed to construct url: %v", err)
}
if fragment != "" {
path += "#" + fragment
}
if file := os.Getenv("TEST_GODOC_URL_FILE"); file != "" {
return os.WriteFile(file, []byte(path+"\n"), 0666)
}
// Turn off the default signal handler for SIGINT (and SIGQUIT on Unix)
// and instead wait for the child process to handle the signal and
// exit before exiting ourselves.
base.StartSigHandlers()
// Prepend the local download cache to GOPROXY to get around deprecation checks.
env := os.Environ()
vars, err := runCmd(env, goCmd(), "env", "GOPROXY", "GOMODCACHE")
fields := strings.Fields(vars)View on GitHub (pinned to b6b368adc5)
Solutions
- Sanitize urlPath to a plain relative path before calling doPkgsite.
- Pass any fragment via the dedicated `fragment` argument, not inside urlPath.
- Report it as a go bug if reached with normal `go doc` usage.
- As a workaround, invoke pkgsite directly.
Example fix
// before: urlPath carries a fragment or full URL -> JoinPath fails
path, err := url.JoinPath("http://"+addr, urlPath) // urlPath="http://x/#y"
// after: keep urlPath relative; pass fragment separately
path, err := url.JoinPath("http://"+addr, strings.TrimPrefix(urlPath, "/"))
if fragment != "" { path += "#" + fragment } Defensive patterns
Strategy: validation
Validate before calling
// strip scheme and fragments; pass fragment separately
rel := strings.TrimPrefix(urlPath, "http://")
rel = strings.TrimPrefix(rel, "https://")
if i := strings.IndexByte(rel, '#'); i >= 0 {
fragment = rel[i+1:]
rel = rel[:i]
}
// then build the URL safely Type guard
func isSafeUrlPath(p string) bool {
return !strings.ContainsAny(p, ":#\n\r") && !strings.HasPrefix(p, "http")
} Prevention
- Keep urlPath a plain relative path; pass fragments via the fragment arg.
- Never feed full URLs into the path component.
- Treat this error as a bug signal — it should be unreachable in normal use.
When it happens
Trigger: urlPath contains characters/sequences that url.JoinPath rejects (e.g. an absolute URL, control chars, or a malformed fragment baked into urlPath instead of the fragment argument). In normal use this should be unreachable.
Common situations: A bug in how go doc derives urlPath from user args (e.g. passing a full URL or a path with embedded newlines); future refactors that violate JoinPath's invariants.
Related errors
- failed to find port for documentation server: %v
- non-file URL
- file URL missing path
- file URL specifies non-local host
- file URL encodes volume in host field: too few slashes?
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/7edef2b8bc686cf6.
Report an issue: GitHub.