The Problem
Go lets you embed structs to share fields and methods easily. It feels like magic until two embedded structs share a field with the same name. When this happens, the Go compiler can't decide which one you want to "promote" to the parent level. It gives up and throws this error:
./main.go:25:12: ambiguous selector p.Name
TL;DR: The Quick Fix
You need to tell the compiler exactly which path to take. Instead of calling the field directly from the parent, use the embedded type's name as a bridge.
// ❌ This triggers the error
fmt.Println(p.Name)
// ✅ Fix: Be specific
fmt.Println(p.User.Name)
// OR
fmt.Println(p.Admin.Name)
Why This Happens
Go uses a feature called promotion. When you embed a struct anonymously, Go tries to surface those internal fields so they look like they belong to the parent. This keeps your code clean and avoids deep nesting.
Trouble starts when two or more embedded structs at the same depth share a member name. Imagine the compiler looking at the parent struct and seeing two different roads to a field called Name. It won't guess which one you meant. To prevent silent bugs, the compiler stops the build immediately.
A Real-World Example
In this snippet, a Profile struct embeds both User and Account. Since both define a Name field, p.Name becomes an illegal move.
package main
import "fmt"
type User struct {
Name string
}
type Account struct {
Name string
}
type Profile struct {
User
Account
}
func main() {
p := Profile{}
p.User.Name = "Jane Doe"
p.Account.Name = "Premium_User_99"
// Error: ambiguous selector p.Name
fmt.Println(p.Name)
}
Fix 1: Use Explicit Qualification
The fastest way out is to skip the promotion. Even when you embed structs anonymously, their type names still act as field names for access. This makes your intent clear to anyone reading the code later.
func main() {
p := Profile{
User: User{Name: "Alice"},
Account: Account{Name: "Billing_Dept"},
}
// Tell the compiler exactly which 'Name' to fetch
fmt.Println("User:", p.User.Name)
fmt.Println("Account:", p.Account.Name)
}
Fix 2: Shadowing (Overriding)
If you want to keep using p.Name, define that field directly on the parent struct. This is called "shadowing." Go's resolution logic checks the top-level struct first. If it finds the field there, it stops looking deeper, which effectively hides the conflicts below.
type Profile struct {
User
Account
Name string // This field wins and shadows the others
}
func main() {
p := Profile{Name: "Primary Identity"}
// This now works perfectly
fmt.Println(p.Name)
// The embedded fields are still there if you need them
fmt.Println(p.User.Name)
}
Fix 3: Resolving Method Conflicts
This isn't just about data. If User and Account both have a Save() method, p.Save() will fail. You can fix this by adding a Save() method to Profile that acts as a traffic controller.
func (p *Profile) Save() {
// Manually decide which methods to run
p.User.Save()
p.Account.Save()
}
Fix 4: Switch to Named Fields
If the ambiguity makes your code messy, embedding might be the wrong tool for the job. You can refactor to use standard named fields. This removes promotion entirely, forcing you to be explicit every single time you access the data.
type Profile struct {
Owner User // Not embedded
Billing Account // Not embedded
}
// Usage is now unambiguous:
// p.Owner.Name
How to Verify the Fix
Follow these three steps to ensure your code is solid:
- **Run the Build:** Execute `go build`. If the "ambiguous selector" error vanishes, your syntax is valid.
- **Test the Logic:** Print the values to the console. A common mistake is shadowing a field but forgetting to update the data in the underlying embedded struct.
- **Check Your IDE:** Tools like VS Code or GoLand usually highlight these conflicts with red squiggly lines. If the red lines are gone, you're in the clear.
Summary
The ambiguous selector error is just Go's way of asking for directions. By using explicit paths or shadowing fields at the top level, you give the compiler the clarity it needs to build a predictable binary.

