Go: panic: runtime error: invalid memory address or nil pointer dereference

Go

Go Problem Overview


When running my Go program, it panics and returns the following:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x38 pc=0x26df]

goroutine 1 [running]:
main.getBody(0x1cdcd4, 0xf800000004, 0x1f2b44, 0x23, 0xf84005c800, ...)
        /Users/matt/Dropbox/code/go/scripts/cron/fido.go:65 +0x2bb
main.getToken(0xf84005c7e0, 0x10)
        /Users/matt/Dropbox/code/go/scripts/cron/fido.go:140 +0x156
main.main()
        /Users/matt/Dropbox/code/go/scripts/cron/fido.go:178 +0x61

goroutine 2 [syscall]:
created by runtime.main
        /usr/local/Cellar/go/1.0.3/src/pkg/runtime/proc.c:221

goroutine 3 [syscall]:
syscall.Syscall6()
        /usr/local/Cellar/go/1.0.3/src/pkg/syscall/asm_darwin_amd64.s:38 +0x5
syscall.kevent(0x6, 0x0, 0x0, 0xf840085188, 0xa, ...)
        /usr/local/Cellar/go/1.0.3/src/pkg/syscall/zsyscall_darwin_amd64.go:199 +0x88
syscall.Kevent(0xf800000006, 0x0, 0x0, 0xf840085188, 0xa0000000a, ...)
        /usr/local/Cellar/go/1.0.3/src/pkg/syscall/syscall_bsd.go:546 +0xa4
net.(*pollster).WaitFD(0xf840085180, 0xf840059040, 0x0, 0x0, 0x0, ...)
        /usr/local/Cellar/go/1.0.3/src/pkg/net/fd_darwin.go:96 +0x185
net.(*pollServer).Run(0xf840059040, 0x0)
        /usr/local/Cellar/go/1.0.3/src/pkg/net/fd.go:236 +0xe4
created by net.newPollServer
        /usr/local/Cellar/go/1.0.3/src/pkg/net/newpollserver.go:35 +0x382

I've looked at the responses others have had to the same exception, but can't see anything simple (i.e. an unhandled error).

I am running it on a machine that does not have access to the API servers listed in the code, but I was hoping it'd return an appropriate error (as I've attempted to catch errors of that kind).

package main

/*
Fido fetches the list of public images from the Glance server, captures the IDs of images with 'status': 'active' and then queues the images for pre-fetching with the Glance CLI utility `glance-cache-manage`. Once the images are added to the queue, `glance-cache-prefetcher` is called to actively fetch the queued images into the local compute nodes' image cache.

See http://docs.openstack.org/developer/glance/cache.html for further details on the Glance image cache.
*/

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	/*
		"log"
		"log/syslog"
	*/
	"net/http"
	"os"
	"os/exec"
)

func prefetchImages() error {

	cmd := exec.Command("glance-cache-prefetcher")
	err := cmd.Run()

	if err != nil {
		return fmt.Errorf("glance-cache-prefetcher failed to execute properly: %v", err)
	}

	return nil
}

func queueImages(hostname string, imageList []string) error {

	for _, image := range imageList {
		cmd := exec.Command("glance-cache-manage", "--host=", hostname, "queue-image", image)
		err := cmd.Run()

		if err != nil {
			return fmt.Errorf("glance-cache-manage failed to execute properly: %v", err)
		} else {
			fmt.Printf("Image %s queued", image)
		}
	}

	return nil
}

func getBody(method string, url string, headers map[string]string, body []byte) ([]byte, error) {

	client := &http.Client{}
	req, err := http.NewRequest(method, url, bytes.NewReader(body))

	if err != nil {
		return nil, err
	}

	for key, value := range headers {
		req.Header.Add(key, value)
	}

	res, err := client.Do(req)
	defer res.Body.Close()

	if err != nil {
		return nil, err
	}

	var bodyBytes []byte

	if res.StatusCode == 200 {
		bodyBytes, err = ioutil.ReadAll(res.Body)
	} else if err != nil {
		return nil, err
	} else {
		return nil, fmt.Errorf("The remote end did not return a HTTP 200 (OK) response.")
	}

	return bodyBytes, nil

}

func getImages(authToken string) ([]string, error) {

	type GlanceDetailResponse struct {
		Images []struct {
			Name   string `json:"name"`
			Status string `json:"status"`
			ID     string `json:"id"`
		}
	}

	method := "GET"
	url := "http://192.168.1.2:9292/v1.1/images/detail"
	headers := map[string]string{"X-Auth-Token": authToken}

	bodyBytes, err := getBody(method, url, headers, nil)

	if err != nil {
		return nil, fmt.Errorf("unable to retrieve the response body from the Glance API server: %v", err)
	}

	var glance GlanceDetailResponse
	err = json.Unmarshal(bodyBytes, &glance)

	if err != nil {
		return nil, fmt.Errorf("unable to parse the JSON response:", err)
	}

	imageList := make([]string, 10)

	for _, image := range glance.Images {
		if image.Status == "active" {
			imageList = append(imageList, image.ID)
		}
	}

	return imageList, nil

}

func getToken() (string, error) {

	type TokenResponse struct {
		Auth []struct {
			Token struct {
				Expires string `json:"expires"`
				ID      string `json:"id"`
			}
		}
	}

	method := "POST"
	url := "http://192.168.1.2:5000/v2.0/tokens"
	headers := map[string]string{"Content-type": "application/json"}
	creds := []byte(`{"auth":{"passwordCredentials":{"username": "glance", "password":"<password>"}, "tenantId":"<tenantkeygoeshere>"}}`)

	bodyBytes, err := getBody(method, url, headers, creds)

	if err != nil {
		return "", err
	}

	var keystone TokenResponse
	err = json.Unmarshal(bodyBytes, &keystone)

	if err != nil {
		return "", err
	}

	authToken := string((keystone.Auth[0].Token.ID))

	return authToken, nil
}

func main() {

	/*
		slog, err := syslog.New(syslog.LOG_ERR, "[fido]")

		if err != nil {
			log.Fatalf("unable to connect to syslog: %v", err)
			os.Exit(1)
		} else {
			defer slog.Close()
		}
	*/

	hostname, err := os.Hostname()

	if err != nil {
		// slog.Err("Hostname not captured")
		os.Exit(1)
	}

	authToken, err := getToken()

	if err != nil {
		// slog.Err("The authentication token from the Glance API server was not retrieved")
		os.Exit(1)
	}

	imageList, err := getImages(authToken)

	err = queueImages(hostname, imageList)

	if err != nil {
		// slog.Err("Could not queue the images for pre-fetching")
		os.Exit(1)
	}

	err = prefetchImages()

	if err != nil {
		// slog.Err("Could not queue the images for pre-fetching")
		os.Exit(1)
	}

	return
}

Go Solutions


Solution 1 - Go

According to the docs for func (*Client) Do:

>"An error is returned if caused by client policy (such as CheckRedirect), or if there was an HTTP protocol error. A non-2xx response doesn't cause an error. > >When err is nil, resp always contains a non-nil resp.Body."

Then looking at this code:

res, err := client.Do(req)
defer res.Body.Close()

if err != nil {
    return nil, err
}

I'm guessing that err is not nil. You're accessing the .Close() method on res.Body before you check for the err.

The defer only defers the function call. The field and method are accessed immediately.


So instead, try checking the error immediately.

res, err := client.Do(req)

if err != nil {
    return nil, err
}
defer res.Body.Close()

Solution 2 - Go

The nil pointer dereference is in line 65 which is the defer in

res, err := client.Do(req)
defer res.Body.Close()

if err != nil {
    return nil, err
}

If err!= nil then res==nil and res.Body panics. Handle err before defering the res.Body.Close().

Solution 3 - Go

Since I got here with my problem I will add this answer although it is not exactly relevant to the original question. When you are implementing an interface make sure you do not forget to add the type pointer on your member function declarations. Example:

type AnimalSounder interface {
    MakeNoise()
}

type Dog struct {
    Name string
    mean bool
    BarkStrength int
}
    
func (dog *Dog) MakeNoise() {
    //implementation
}

I forgot the (dog *Dog) part, I do not recommend it. Then you get into ugly trouble when calling MakeNoise on an AnimalSounder interface variable of type Dog.

Solution 4 - Go

I know this might be a coding question, but for others who were looking for another answer, the issue was that I have the program running in powershell and closed powershell without killing the process. The process was using the same port so it was failing with the same error above. I have to manually kill the process and then it worked fine.

Solution 5 - Go

Make sure that you handle all the errors by sending a return value.

`if err!=nil{
    return nil, err
 }`

Solution 6 - Go

for me one solution for this problem was to add in sql.Open ... sslmode=disable

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionelithrarView Question on Stackoverflow
Solution 1 - Gouser1106925View Answer on Stackoverflow
Solution 2 - GoVolkerView Answer on Stackoverflow
Solution 3 - GoBjarki BView Answer on Stackoverflow
Solution 4 - GoMohy EldeenView Answer on Stackoverflow
Solution 5 - GoKalyan KoppuravuriView Answer on Stackoverflow
Solution 6 - GoKaxaView Answer on Stackoverflow