Skip to content

Fix file reading in cmd/jwt #450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

travisnewhouse
Copy link

File reading by cmd/jwt is failing. Inside of loadData(), the os.File was being closed before its contents were read by io.ReadAll(). This leads errors like the following.

$ go run github.com/golang-jwt/jwt/v5/cmd/[email protected] -alg EdDSA -key privateKey.pem -sign jwt.json 
Error: couldn't read token: read /tmp/access-jwt.json: file already closed
exit status 1

The solution is to defer the close operation until the end of the function, which is the way the code had been written prior to PR #438.

Inside of `loadData()`, the `os.File` was being closed before its
contents were read by `io.ReadAll()`.  Defer the close operation
until the end of the function.
if err := f.Close(); err != nil {
return nil, err
}
defer f.Close()
Copy link
Collaborator

@oxisto oxisto Jul 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to do something like

defer func() {
if err := f.Close(); err != nil {
 // Somehow "handle" the error
}
}()

to silence the linter

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way that it can be handled to allow returning the error value to the caller, as well as other errors, is to use a helper function like this one with a named return value;

// Helper func:  Pass in the function directly rather than calling the function
func joinErr(err *error, op func() error) {
  *err = errors.Join(*err, op())
}

// Helper func:  Read input from specified file or stdin
func loadData(p string) (_ []byte, err error) {
	if p == "" {
		return nil, fmt.Errorf("no path specified")
	}

	var rdr io.Reader
	switch p {
	case "-":
		rdr = os.Stdin
	case "+":
		return []byte("{}"), nil
	default:
		f, err := os.Open(p)
		if err != nil {
			return nil, err
		}
		rdr = f
		defer joinErr(&err, f.Close)
	}
	return io.ReadAll(rdr)
}

VictoriaMetrics has a good blogpost on this as well as Uber having run into a similar issue and creating an "Invoker" interface.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a note, the function needs to use a named return value, which is a first for me (I've not really used named returns before), otherwise the deferred function updates the error value that is scoped to the function (and is already discarded)

Details here; https://gophers.slack.com/archives/C02A8LZKT/p1754303913406599

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants