{"record":{"id":"7edef2b8bc686cf6","repo":"golang/go","slug":"internal-error-failed-to-construct-url-v","errorCode":null,"errorMessage":"internal error: failed to construct url: %v","messagePattern":"internal error: failed to construct url: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/doc/pkgsite.go","lineNumber":112,"sourceCode":"\tb.Do(ctx, a)\n\n\t// Both paths return an executable in GOCACHE: CachedExecutable is set on\n\t// fresh builds, while BuiltTarget is set on cache hits.\n\tif cached := a.CachedExecutable(); cached != \"\" {\n\t\treturn cached\n\t}\n\treturn a.BuiltTarget()\n}\n\nfunc doPkgsite(ctx context.Context, urlPath, fragment string) error {\n\tport, err := pickUnusedPort()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to find port for documentation server: %v\", err)\n\t}\n\taddr := fmt.Sprintf(\"localhost:%d\", port)\n\tpath, err := url.JoinPath(\"http://\"+addr, urlPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"internal error: failed to construct url: %v\", err)\n\t}\n\tif fragment != \"\" {\n\t\tpath += \"#\" + fragment\n\t}\n\n\tif file := os.Getenv(\"TEST_GODOC_URL_FILE\"); file != \"\" {\n\t\treturn os.WriteFile(file, []byte(path+\"\\n\"), 0666)\n\t}\n\n\t// Turn off the default signal handler for SIGINT (and SIGQUIT on Unix)\n\t// and instead wait for the child process to handle the signal and\n\t// exit before exiting ourselves.\n\tbase.StartSigHandlers()\n\n\t// Prepend the local download cache to GOPROXY to get around deprecation checks.\n\tenv := os.Environ()\n\tvars, err := runCmd(env, goCmd(), \"env\", \"GOPROXY\", \"GOMODCACHE\")\n\tfields := strings.Fields(vars)","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/doc/pkgsite.go#L94-L130","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: urlPath carries a fragment or full URL -> JoinPath fails\npath, err := url.JoinPath(\"http://\"+addr, urlPath) // urlPath=\"http://x/#y\"\n\n// after: keep urlPath relative; pass fragment separately\npath, err := url.JoinPath(\"http://\"+addr, strings.TrimPrefix(urlPath, \"/\"))\nif fragment != \"\" { path += \"#\" + fragment }","handlingStrategy":"validation","validationCode":"// strip scheme and fragments; pass fragment separately\nrel := strings.TrimPrefix(urlPath, \"http://\")\nrel = strings.TrimPrefix(rel, \"https://\")\nif i := strings.IndexByte(rel, '#'); i >= 0 {\n    fragment = rel[i+1:]\n    rel = rel[:i]\n}\n// then build the URL safely","typeGuard":"func isSafeUrlPath(p string) bool {\n    return !strings.ContainsAny(p, \":#\\n\\r\") && !strings.HasPrefix(p, \"http\")\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["doc","pkgsite","url","internal"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}