Go: panic: 运行时错误:无效的内存地址或空指针解引用

huangapple go评论81阅读模式
英文:

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

问题

当运行我的Go程序时,它会发生panic并返回以下内容:

panic: 运行时错误:无效的内存地址或nil指针解引用
[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

我已经查看了其他人对相同异常的回答,但没有看到任何简单的解决方法(即未处理的错误)。

我在一台无法访问代码中列出的API服务器的机器上运行它,但我希望它返回一个适当的错误(因为我已经尝试捕获那种错误)。

package main

/*
Fido从Glance服务器获取公共图像列表,捕获具有“status”:“active”的图像的ID,然后使用Glance CLI实用程序“glance-cache-manage”将图像排队进行预取。一旦将图像添加到队列中,“glance-cache-prefetcher”将被调用以主动将排队的图像获取到本地计算节点的图像缓存中。

有关Glance图像缓存的更多详细信息,请参阅http://docs.openstack.org/developer/glance/cache.html。
*/

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"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执行失败:%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执行失败:%v", err)
		} else {
			fmt.Printf("图像%s已排队", 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("远程端没有返回HTTP 200(OK)响应。")
	}

	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("无法从Glance API服务器检索响应正文:%v", err)
	}

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

	if err != nil {
		return nil, fmt.Errorf("无法解析JSON响应:%v", 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() {

	hostname, err := os.Hostname()

	if err != nil {
		os.Exit(1)
	}

	authToken, err := getToken()

	if err != nil {
		os.Exit(1)
	}

	imageList, err := getImages(authToken)

	err = queueImages(hostname, imageList)

	if err != nil {
		os.Exit(1)
	}

	err = prefetchImages()

	if err != nil {
		os.Exit(1)
	}

	return
}
英文:

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 &#39;status&#39;: &#39;active&#39; 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&#39; image cache.
See http://docs.openstack.org/developer/glance/cache.html for further details on the Glance image cache.
*/
import (
&quot;bytes&quot;
&quot;encoding/json&quot;
&quot;fmt&quot;
&quot;io/ioutil&quot;
/*
&quot;log&quot;
&quot;log/syslog&quot;
*/
&quot;net/http&quot;
&quot;os&quot;
&quot;os/exec&quot;
)
func prefetchImages() error {
cmd := exec.Command(&quot;glance-cache-prefetcher&quot;)
err := cmd.Run()
if err != nil {
return fmt.Errorf(&quot;glance-cache-prefetcher failed to execute properly: %v&quot;, err)
}
return nil
}
func queueImages(hostname string, imageList []string) error {
for _, image := range imageList {
cmd := exec.Command(&quot;glance-cache-manage&quot;, &quot;--host=&quot;, hostname, &quot;queue-image&quot;, image)
err := cmd.Run()
if err != nil {
return fmt.Errorf(&quot;glance-cache-manage failed to execute properly: %v&quot;, err)
} else {
fmt.Printf(&quot;Image %s queued&quot;, image)
}
}
return nil
}
func getBody(method string, url string, headers map[string]string, body []byte) ([]byte, error) {
client := &amp;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(&quot;The remote end did not return a HTTP 200 (OK) response.&quot;)
}
return bodyBytes, nil
}
func getImages(authToken string) ([]string, error) {
type GlanceDetailResponse struct {
Images []struct {
Name   string `json:&quot;name&quot;`
Status string `json:&quot;status&quot;`
ID     string `json:&quot;id&quot;`
}
}
method := &quot;GET&quot;
url := &quot;http://192.168.1.2:9292/v1.1/images/detail&quot;
headers := map[string]string{&quot;X-Auth-Token&quot;: authToken}
bodyBytes, err := getBody(method, url, headers, nil)
if err != nil {
return nil, fmt.Errorf(&quot;unable to retrieve the response body from the Glance API server: %v&quot;, err)
}
var glance GlanceDetailResponse
err = json.Unmarshal(bodyBytes, &amp;glance)
if err != nil {
return nil, fmt.Errorf(&quot;unable to parse the JSON response:&quot;, err)
}
imageList := make([]string, 10)
for _, image := range glance.Images {
if image.Status == &quot;active&quot; {
imageList = append(imageList, image.ID)
}
}
return imageList, nil
}
func getToken() (string, error) {
type TokenResponse struct {
Auth []struct {
Token struct {
Expires string `json:&quot;expires&quot;`
ID      string `json:&quot;id&quot;`
}
}
}
method := &quot;POST&quot;
url := &quot;http://192.168.1.2:5000/v2.0/tokens&quot;
headers := map[string]string{&quot;Content-type&quot;: &quot;application/json&quot;}
creds := []byte(`{&quot;auth&quot;:{&quot;passwordCredentials&quot;:{&quot;username&quot;: &quot;glance&quot;, &quot;password&quot;:&quot;&lt;password&gt;&quot;}, &quot;tenantId&quot;:&quot;&lt;tenantkeygoeshere&gt;&quot;}}`)
bodyBytes, err := getBody(method, url, headers, creds)
if err != nil {
return &quot;&quot;, err
}
var keystone TokenResponse
err = json.Unmarshal(bodyBytes, &amp;keystone)
if err != nil {
return &quot;&quot;, err
}
authToken := string((keystone.Auth[0].Token.ID))
return authToken, nil
}
func main() {
/*
slog, err := syslog.New(syslog.LOG_ERR, &quot;[fido]&quot;)
if err != nil {
log.Fatalf(&quot;unable to connect to syslog: %v&quot;, err)
os.Exit(1)
} else {
defer slog.Close()
}
*/
hostname, err := os.Hostname()
if err != nil {
// slog.Err(&quot;Hostname not captured&quot;)
os.Exit(1)
}
authToken, err := getToken()
if err != nil {
// slog.Err(&quot;The authentication token from the Glance API server was not retrieved&quot;)
os.Exit(1)
}
imageList, err := getImages(authToken)
err = queueImages(hostname, imageList)
if err != nil {
// slog.Err(&quot;Could not queue the images for pre-fetching&quot;)
os.Exit(1)
}
err = prefetchImages()
if err != nil {
// slog.Err(&quot;Could not queue the images for pre-fetching&quot;)
os.Exit(1)
}
return
}

答案1

得分: 192

根据func (*Client) Do的文档:

>如果由于客户端策略(例如CheckRedirect)或HTTP协议错误而导致错误,则返回错误。非2xx响应不会导致错误。
>
>当err为nil时,resp始终包含非nil的resp.Body。

然后看一下这段代码:

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

我猜测err不是nil。在检查err之前,你正在访问res.Body上的.Close()方法。

defer只是延迟了函数调用。字段和方法会立即访问。


所以,尝试立即检查错误。

res, err := client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
英文:

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()

答案2

得分: 15

空指针解引用在第65行,即延迟执行的部分

res, err := client.Do(req)
defer res.Body.Close()
如果 err != nil,则 res == nil,res.Body 会引发 panic。

在延迟执行 res.Body.Close() 之前处理 err。

英文:

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().

答案3

得分: 10

自从我遇到这个问题以来,我将添加这个答案,尽管它与原始问题不完全相关。当你实现一个接口时,确保不要忘记在成员函数声明中添加类型指针。例如:

type AnimalSounder interface {
    MakeNoise()
}

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

我忘记了**(dog *Dog)**这部分,我不建议这样做。然后,当在类型为DogAnimalSounder接口变量上调用MakeNoise时,你会遇到麻烦。

英文:

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.

答案4

得分: 0

非常常见的用例
错误:<br> Go: panic: runtime error: invalid memory address or nil pointer dereference

可能由多种原因引起,所有这些原因最终都与内存分配有关。

我将添加另一个非常常见的用例(但对于初学者来说并不简单),这种错误可能会发生:<br> 单元测试

假设您正在为一个特定函数编写单元测试,该函数引用在函数外部初始化的变量。<br>
当您尝试测试此函数时,您将收到恐慌错误。

示例

例如,一个名为ValidateTag的函数在第三方验证库中具有依赖关系:

func ValidateTag(tag string) (bool, error) {
errs := validator.validate(tag) # &lt;--- Validator Struct 应该已经初始化
if errs != nil {
return false,errs
}
return true, nil
}

正如我们所看到的,validator结构体应该在ValidateTag之外初始化,因为它在主代码中的多个位置使用。

失败

因此,如果我们的测试函数不初始化validator结构体:

import &quot;testing&quot;
func TestValidateAction(t *testing.T){
ans, _ := ValidateTag(&quot;Some-Tag&quot;)
if ans != &quot;Bravo&quot; {
t.Errorf(&quot;Error!&quot;)
}
}

我们将得到无效的内存地址错误。

正常工作

在这里,我们导入相关的验证库并初始化在目标函数之外分配的变量:

import (
&quot;testing&quot;
&quot;github.com/some-validator-library/validator&quot;
)
func TestValidateAction(t *testing.T){
validate = validator.New() # &lt;-- 这里
ans, _ := ValidateTag(&quot;Some-Tag&quot;)
if ans != &quot;Bravo&quot; {
t.Errorf(&quot;Error!&quot;)
}
}
英文:

Very common use case

The error: <br> Go: panic: runtime error: invalid memory address or nil pointer dereference

Can be caused by multiple reasons that all of them are eventuially related to memory allocations.

I'll add another very common use case (but not trivial for beginners) that this error might occur: <br> Unit testing.

In case you're writing a unit test for a specific function that is referring to a variable that was initialized outside the function. <br>
When you will try to test this function you will get the panic error.

Example

For example, a function named ValidateTag has a dependency in a 3rd-party validation library:

func ValidateTag(tag string) (bool, error) {
errs := validator.validate(tag) # &lt;--- Validator Struct should already be initialized
if errs != nil {
return false,errs
}
return true, nil
}

Has we can see, the validator struct should be initialized outside the the ValidateTag because it is being used in multiple places in the main code.

Failing

So if our testing function will not initialize the validator struct:

import &quot;testing&quot;
func TestValidateAction(t *testing.T){
ans, _ := ValidateTag(&quot;Some-Tag&quot;)
if ans != &quot;Bravo&quot; {
t.Errorf(&quot;Error!&quot;)
}
}

We will get the invalid memory address error.

Working

Here we're importing the relevant validation library and initializing the variable that was allocated outside the target function:

import (
&quot;testing&quot;
&quot;github.com/some-validator-library/validator&quot;
)
func TestValidateAction(t *testing.T){
validate = validator.New() # &lt;-- Here
ans, _ := ValidateTag(&quot;Some-Tag&quot;)
if ans != &quot;Bravo&quot; {
t.Errorf(&quot;Error!&quot;)
}
}

答案5

得分: -3

我知道这可能是一个编码问题,但对于其他寻找另一个答案的人来说,问题是我在powershell中运行程序并关闭了powershell而没有终止进程。该进程正在使用相同的端口,因此出现了上述相同的错误。我必须手动终止进程,然后它就正常工作了。

英文:

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.

答案6

得分: -5

确保通过返回值处理所有错误。

`if err!=nil{
return nil, err
}`
英文:

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

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

huangapple
  • 本文由 发表于 2013年4月29日 21:41:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/16280176.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定