Runtime error: invalid memory address or nil pointer dereference #1

I am creating a web app using golang :

I created package main

package main import (“fmt” “io/ioutil” “net/http”)

defined page as a struct with 2 files
type page struct{ Title string Body []byte // byte slice }

created the save method

func (p*page) save() error { isdoC := p.Title + “.txt” return ioutil.WriteFile(isdoC, p.Body, 0600) }

created the load page functionality

func loadpage(title string) (*page, error) { isdoC := title + “.txt” body, err := ioutil.ReadFile(isdoC) if err != nil { return nil, err } return &page{Title: title, Body: body}, nil }

func viewHandler(w http.ResponseWriter, r*http.Request) { title := r.URL.Path[len("/view/"):] p, _ := loadpage(title) fmt.Println(w, “

s

%s
”, p.Title, p.Body) }

func main() { http.HandleFunc("/view/", viewHandler) http.ListenAndServe(":8080", nil) }

How when I compile and ran the code I got a : <runtime error: invalid memory address or nil pointer

dereference>

WHAT am I doing wrong?

Your code and errors are almost impossible to read. Please wrap code by triple backticks above and below,

like this

to make it a code block

2 Likes

I made it for you:

package main 

import (
	"fmt" 
	"io/ioutil" 
	"net/http"
)

type page struct { 
	Title string 
	Body []byte 
}

func (p *page) save() error { 
	isdoC := p.Title + ".txt" 
	return ioutil.WriteFile(isdoC, p.Body, 0600) 
}
func loadpage(title string) (*page, error) { 
	isdoC := title + ".txt" 
	body, err := ioutil.ReadFile(isdoC) 
	if err != nil { 
		return nil, err 
	} 
	return &page{Title: title, Body: body}, nil 
}

func viewHandler(w http.ResponseWriter, r*http.Request) { 
	title := r.URL.Path[len("/view/"):] 
	p, _ := loadpage(title) 
	fmt.Println(w, "%s%s", p.Title, p.Body) 
}
func main() { 
	http.HandleFunc("/view/", viewHandler) 
	http.ListenAndServe(":8080", nil) 
}

Hey @iivri,

The panic here is the result of you ignoring the error from loadpage. You’ll need to replace it with something like the following to fix it:

p, err := loadpage(title)
if err != nil {
	http.Error(w, err.Error(), http.StatusNotFound)
	return
}

When you are calling loadpage on a file that doesn’t yet exist and ioutil.ReadFile returns an error here:

body, err := ioutil.ReadFile(isdoC)
if err != nil {
	return nil, err
}

loadpage is returning a nil pointer to a page object and you’re then trying to dereference it’s Title and Body which is causing the panic.

2 Likes

@radovskyb So I should change

func loadpage(title string) (*page, error)  {
        isdoC := title + ".txt"
        // but what if ReadFile encounters an error?
        body, err := ioutil.ReadFile(isdoC)
        if err != nil {
              return nil, err
       }
       return &page{Title: title, Body: body}, nil
}

To what you wrote?

Hey @iivri,

There’s nothing wrong with your loadpage function, but rather where you call it.

You should change this part in your code from the viewHandler function:

p, _ := loadpage(title)

To this:

p, err := loadpage(title)
if err != nil {
	http.Error(w, err.Error(), http.StatusNotFound)
	return
}

@radovskyb Yes !! It worked!!! THANK you so much. I am completely new to programming and this was really dissuading me from actually learning how to program. You help is really appreciated. Incidentally HOW do you get the Yellow box around your code like that ? lol

@radovskyb It worked but it only said page not found 404. It is supposed to display some page data for the test.txt file I created. HOW would I do that?

Glad to help!

After you paste or type your code in the editing box where I’m typing now, highlight the code you want in the box and then click the little </> icon. You can also simply indent all of your code by 4 spaces and it will automatically be boxed as well.

@radovskyb Ok. I will do that. Also It worked but it only said page not found 404. It is supposed to display some page data for the test.txt file I created. HOW would I do that?

Ok, so basically, you are now receiving that error because you are trying to load a page that doesn’t exist yet and the code I added for you lets you know that the page is missing with an http 404 error.

If I recall correctly, you are currently learning from this page: https://golang.org/doc/articles/wiki/, which in that case I simply suggest following and reading it all the way to the end properly because further down in the article you’ll even see that they add similar error handling to what I wrote for you earlier.

If you are positive that you have created the test.txt file in the correct place, make sure you are going to the correct link. The way the code is currently written, to view test.txt, you should be going to http://localhost:8080/view/test, but maybe you are accidentally going to http://localhost:8080/view/test.txt instead?

@radovskyb I entered http://localhost:8080/view/test exactly but now it is telling me that open test.txt: no such file or director.

@iivri, it sounds like to me, that you have either created test.txt in the wrong directory or you are running your program from the wrong directory.

Try to make sure you are following that blog post properly because it should tell you what folder structure you should have set up and how to correctly run your program.

For example, if you are trying to open a file in the same directory where your code is located, but you run the program from outside of that folder, it will not work properly.

One other thing since you just mentioned before that you are new to programming.

If you have not yet already, I would highly suggest going through and learning the go tour: https://tour.golang.org/welcome/1.

@radovskyb THE test.txt file is actually in the same folder that the code is located as main.go,

Yeah, there’s no problem with that, but what I’m suggesting is that you make sure that you are actually running go run main.go from inside of the folder when you run your program, or if you are using go build or go install, when you actually run the executable for the program, that you are inside of the same folder :slight_smile:

If you are unsure if your program is being called from the correct folder, add something like this to your main function and check if the printed location is the same as the location where your test.txt file is being stored.

package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	cwd, err := os.Getwd()
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(cwd)
}

@radovskyb Ok. I did that and this is the result

&{0xc420092280 0xc4200dc0f0 0x376f90 0x171d00 false false false false 0xc42006e540 {0xc4200776c0 map[] false false} map[] false 0 -1 0 false false [] 0 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] <nil>} <h1>s</h1><div>%s</div> test [73 32 97 109 32 111 110 32 105 115 67 111 110 113 117 101 115 116 10]

Oh yep, no problem, the issue now is that you are accidentally using fmt.Println, instead of fmt.Fprintf :slight_smile:

Replace:

fmt.Println(w, "%s%s", p.Title, p.Body)

With:

fmt.Fprintf(w, "%s%s", p.Title, p.Body)

With fmt.Println you are accidentally printing out the http.ResponseWriter object w which is why all of that stuff showed up, instead of with using fmt.Fprintf it will print the formatted result TO w which is a writer.

@radovskyb lol Now http://localhost:8080/view/test now displays this.