netbirdio/netbird · error
id %q is not valid
Error message
id %q is not valid
What it means
getProfileConfigPath — backing the exported GetConfigPath and used by LogoutProfile, RemoveProfile, and the email lookup — rejects ids that fail IsValidProfileFilenameStem: empty, over 64 characters, containing '/', '\', '..', or characters outside letters/digits/'_'/'-'. This is the package's path-traversal guard: no filesystem path is built from an unvalidated id. "default" is valid and maps to configDir/netbird.cfg instead of profiles/default.json.
Source
Thrown at client/android/profile_manager.go:252
}
// The account file is this package's, not the ServiceManager's, so it must
// go here. The default profile has a fixed filename, so a recreated one
// would otherwise inherit the deleted profile's email as its login_hint.
// Not fatal: the profile itself is gone.
if err := removeProfileEmail(configPath); err != nil {
log.Warnf("failed to remove stored account email for profile %s: %v", id, err)
}
log.Infof("removed profile: %s", id)
return nil
}
// getProfileConfigPath returns the config file path for a profile
// This is needed for Android-specific path handling (netbird.cfg for default profile)
func (pm *ProfileManager) getProfileConfigPath(id string) (string, error) {
if !profilemanager.IsValidProfileFilenameStem(profilemanager.ID(id)) {
return "", fmt.Errorf("id %q is not valid", id)
}
if id == profilemanager.DefaultProfileName {
// Android uses netbird.cfg for default profile instead of default.json
// Default profile is stored in root configDir, not in profiles/
return filepath.Join(pm.configDir, defaultConfigFilename), nil
}
profilesDir := filepath.Join(pm.configDir, profilesSubdir)
return filepath.Join(profilesDir, id+".json"), nil
}
// GetConfigPath returns the config file path for a given profile id
// Java should call this instead of constructing paths with Preferences.configFile()
func (pm *ProfileManager) GetConfigPath(id string) (string, error) {
return pm.getProfileConfigPath(id)
}
View on GitHub (pinned to 93e97f4bf1)
Solutions
- Pass ids exactly as returned by ListProfiles or GetActiveProfile
- Use "default" for the default profile
- Validate free-form input against the stem rules (letters, digits, '_', '-', max 64) before calling
Example fix
// before
path, err := pm.GetConfigPath("work.json") // dots are invalid in a stem
// after
path, err := pm.GetConfigPath("default") // or an id obtained from ListProfiles Defensive patterns
Strategy: validation
Validate before calling
// Gate every path API on a stem-valid id
if !isValidProfileID(id) {
return errors.New("reject before calling GetConfigPath")
} Type guard
func isValidProfileID(id string) bool {
if id == "" || len(id) > 64 {
return false
}
if strings.ContainsAny(id, `/") || strings.Contains(id, "..") {
return false
}
if filepath.Base(id) != id {
return false
}
for _, r := range id {
if !(unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' || r == '-') {
return false
}
}
return true
} Try / catch
path, err := pm.GetConfigPath(id)
if err != nil && strings.Contains(err.Error(), "is not valid") {
// caller bug: a display name/filename was passed instead of a profile id
} Prevention
- Pass ids verbatim from ListProfiles/GetActiveProfile; never construct them
- Use "default" for the default profile — it maps to netbird.cfg
- Remember '.' is invalid in a stem: "work.json" must be passed as "work"
When it happens
Trigger: GetConfigPath(id) — or LogoutProfile/RemoveProfile internally — with a display name, a filename like "work.json" (the dot is not a legal stem character), or any free-form string containing spaces, slashes, or dots.
Common situations: Java layer migrating off Preferences.configFile() passing constructed filenames instead of profile ids; ids from older app versions with unsanitized names.
Related errors
- id '%s' is not valid
- failed to add profile: %w
- failed to rename profile: %w
- failed to list profiles: %w
- failed to get active profile: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/94c90c01044b5044.
Report an issue: GitHub.