179 votes

Go : panic : runtime error : adresse mémoire invalide ou déréférencement de pointeur nul

Lorsque je lance mon programme Go, il panique et renvoie le message suivant :

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

J'ai examiné les réponses que d'autres ont apportées à la même exception, mais je ne vois rien de simple (c'est-à-dire une erreur non gérée).

Je l'exécute sur une machine qui n'a pas accès aux serveurs API répertoriés dans le code, mais j'espérais qu'il renverrait une erreur appropriée (car j'ai essayé de détecter les erreurs de ce type).

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
}

197voto

Selon les docs pour func (*Client) Do :

"Une erreur est renvoyée si elle est causée par la politique du client (telle que CheckRedirect), ou s'il y a eu une erreur de protocole HTTP. Une réponse non-2xx ne provoque pas d'erreur.

Lorsque err est nul, resp contient toujours un resp.Body non nul."

Puis en regardant ce code :

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

if err != nil {
    return nil, err
}

Je suppose que err n'est pas nil . Vous accédez au .Close() méthode sur res.Body avant de vérifier si le err .

El defer ne fait que reporter l'appel de la fonction. L'accès au champ et à la méthode est immédiat.


Essayez plutôt de vérifier l'erreur immédiatement.

res, err := client.Do(req)

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

15voto

Volker Points 6807

Le déréférencement du pointeur nul se fait à la ligne 65 qui est le defer dans

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

if err != nil {
    return nil, err
}

Si err!= nil alors res==nil et res.Body panique. Traitez err avant de différer le res.Body.Close().

11voto

Bjarki B Points 91

Puisque je suis arrivé ici avec mon problème, je vais ajouter cette réponse bien qu'elle ne soit pas exactement pertinente à la question initiale. Lorsque vous implémentez une interface, n'oubliez pas d'ajouter le pointeur de type dans vos déclarations de fonctions membres. Exemple :

type AnimalSounder interface {
    MakeNoise()
}

type Dog struct {
    Name string
    mean bool
    BarkStrength int
}

func (dog *Dog) MakeNoise() {
    //implementation
}

J'ai oublié le (chien *Chien) partie, je ne le recommande pas. Vous aurez alors de gros problèmes lorsque vous appellerez MakeNoise sur un AnimalSounder variable d'interface de type Dog .

-3voto

Mohy Eldeen Points 131

Je sais que c'est peut-être une question de codage, mais pour les autres qui cherchaient une autre réponse, le problème était que j'avais le programme en cours d'exécution dans powershell et fermé powershell sans tuer le processus. Le processus utilisait le même port et échouait donc avec la même erreur que ci-dessus. J'ai dû tuer manuellement le processus et cela a fonctionné correctement.

-5voto

Assurez-vous de traiter toutes les erreurs en envoyant une valeur de retour.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X