Skip to content

os/user: Lookup does not handle windows.ERROR_NONE_MAPPED correctly #73595

Open
@maja42

Description

@maja42

Go version

1.23.1, windows/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE='on'
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/jma/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/jma/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build134067901=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/jma/goShared/go-utils/go.mod'
GOMODCACHE='/home/jma/.gvm/pkgsets/go1.24.1/global/pkg/mod'
GOOS='linux'
GOPATH='/home/jma/.gvm/pkgsets/go1.24.1/global'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/jma/.gvm/gos/go1.24.1'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/jma/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/home/jma/.gvm/gos/go1.24.1/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.1'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

The godoc of os/user.Lookup states the following:

// Lookup looks up a user by username. If the user cannot be found, the
// returned error is of type [UnknownUserError].

However, this is not correct on Windows. When tested on Windows Server 2016 / Windows Server 2019 / Windows Server 2022, the Lookup-method fails with the following error message:

No mapping between account names and security IDs was done.

I expected the function to return user.UnknownUserError.

The error message stems from the Windows Error Code windows.ERROR_NONE_MAPPED - which specifically states that the account name does not exist.

I tried to manually detect this specific error situation with the following workaround:

func CheckUsernameExists(username string) (bool, error) {
	_, err := user.Lookup(username)
	if err == nil {
		return true, nil // user exists
	}
	if _, ok := err.(user.UnknownUserError); ok {
		return false, nil // user does not exist
	}

	// Workaround:
	var sysErr syscall.Errno
	if ok := errors.As(err, &sysErr); ok && sysErr == windows.ERROR_NONE_MAPPED { // "No mapping between account names and security IDs was done."
		return false, nil // does not exist
	}
	return false, err // unknown error
}

However, this does not work either, because user.Lookup does not "wrap" the underlying error (it uses %v instead of %w - also for other target platforms).

What did you see happen?

user.Lookup should return user.UnknownUserError if Windows produces windows.ERROR_NONE_MAPPED.
user.Lookup should also wrap underlying system errors, so that error codes can be inspected.

What did you expect to see?

user.Lookup embeds the windows error message (string) into the returned error message.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.NeedsFixThe path to resolution is known, but the work has not been done.OS-Windows

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions