Package Names Shadowed by Variables in Go and the Fix
A local variable named url, path or time hides the imported package, and the error says the string has no method Parse. See the cause and three fixes.

This is the compile error that makes no sense the first time. You wrote url := ..., then url.Parse(...), and the compiler says a string has no method Parse. You know net/url has a Parse function. The problem is that your variable and the package have the same name, and the variable won. This is a short tip about reading that error and the three ways to avoid it. It is a close relative of the shadowing trap and part of the Go basics path.
Tested with Go 1.25.6, macOS on Apple Silicon (arm64)
What happens
An import declares the package name in the file's scope. A local declaration with the same name, in a function, hides it from that point to the end of the block. The compiler then looks up url and finds your string, not the package:
package main
import (
"fmt"
"net/url"
"path"
"time"
)
func main() {
url := "https://example.test/a?b=1"
u, err := url.Parse(url)
fmt.Println(u, err)
path := "/tmp/x.txt"
fmt.Println(path.Base(path))
time := 5
fmt.Println(time.Now())
}# example.test/lab
./main.go:5:2: "net/url" imported and not used
./main.go:6:2: "path" imported and not used
./main.go:7:2: "time" imported and not used
./main.go:12:16: url.Parse undefined (type string has no field or method Parse)
./main.go:16:19: path.Base undefined (type string has no field or method Base)
./main.go:19:19: time.Now undefined (type int has no field or method Now)There are two kinds of message. The first three lines say each import is imported and not used, which is true: after the shadowing, nothing in the function refers to the package. The other three are the ones that confuse: url.Parse undefined (type string has no field or method Parse), and the same for path.Base on a string and time.Now on an int. Read them as "this identifier is now a variable of type X, and X has no such member". The compiler is following the scoping rules correctly, and the message describes what it found.
Notice that the shadowing starts at the declaration. In the first block url.Parse(url) fails because url was declared on the previous line. A function that uses the package first and declares a variable later compiles fine, and then breaks the next time someone adds a package call after the declaration, which is why this error appears during edits.
The fixes
Rename the variable. Almost always the right answer, and a descriptive name is better anyway. url for a string says nothing about what it holds. rawURL, endpoint and target do:
package main
import (
"fmt"
"net/url"
"path"
"time"
)
func main() {
rawURL := "https://example.test/a?b=1"
u, err := url.Parse(rawURL)
fmt.Println(u.Host, u.Query().Get("b"), err)
filePath := "/tmp/x.txt"
fmt.Println(path.Base(filePath))
elapsed := 5 * time.Second
fmt.Println(elapsed, time.Duration(0))
}example.test 1 <nil>
x.txt
5s 0sThe pattern of naming a variable after its type or its package is a habit from other languages, and the packages in the standard library are named for convenience when typing url.Parse, not to be reused as variable names. A short list of the usual victims: url, path, time, log, errors, strings, bytes, sort, os, list, json and fmt. A variable called list that shadows container/list is common, and so is log for a *log.Logger.
Alias the import. When the package name is the one you want to keep and the variable name is dictated elsewhere, for example a struct field or a function parameter that must be called path, rename the import at the top of the file:
import (
neturl "net/url"
)The alias costs a word at each call and avoids the collision. It is also the right tool when two packages have the same name, such as crypto/rand and math/rand.
Move the declaration into a smaller scope. Shadowing lasts to the end of the block, so a variable declared inside an if or a helper function only hides the package there. This is not a substitute for a good name, and it reduces the damage when you cannot rename.
Why the tools do not warn
You might expect go vet to warn about a variable that hides a package, and it does not, because shadowing is legal and the shadow analyzer from golang.org/x/tools only tracks variables and not imports. The compiler error is your signal, and in an editor with gopls the diagnostic appears as you type. A linter such as revive has an import-shadowing rule that reports exactly this, and enabling it turns the problem into a lint failure, which is worth doing in a team where the habit keeps appearing.
When the collision is deliberate
A function parameter named path in a function that never uses the path package is harmless, and the compiler is happy: the shadowing only matters if you use the package in the same scope. The standard library has such cases in files that do not import the package they share a name with. The rule of thumb that avoids the problem is to scan the file's imports before naming a variable, or to choose names that describe the value and not its type. The specification on declarations and scope says a package name is in the file block, and an inner declaration of the same name takes precedence, and the broader rules for the compiler's error messages about names are in undefined name errors.


