GoLang – bbolt – invalid memory address or nil pointer dereference

Issue

I am trying to use file database bbolt as a key/value storage. Below is my code


package handler

import (
    "encoding/json"
    "log"
    "net/http"
    "os"

    "go.etcd.io/bbolt"
    bolt "go.etcd.io/bbolt"
    yml "gopkg.in/yaml.v3"
)

type urlDB struct {
    db *bbolt.DB
}

func (u urlDB) ensureDB() {
    u.db, _ = bolt.Open("url.db", 0600, nil)
}


func MapHandler(pathsToURLs map[string]string, fallback http.Handler) http.HandlerFunc {

    return func(w http.ResponseWriter, r *http.Request) {
        path := r.URL.Path
        if _, ok := pathsToURLs[path]; ok {
            http.Redirect(w, r, pathsToURLs[path], http.StatusFound)

        } else {
            fallback.ServeHTTP(w, r)
        }
    }

}


func DefaultMap(fallback http.Handler) http.HandlerFunc {
    db := urlDB{}
    db.ensureDB()
    //bkt := db.db.createBucket()
    //createSampleData(bkt)
    defer db.db.Close()

    bktName := "URLBucket"

    createBucket(db.db, bktName)
    addSampleData(db.db, bktName, "/gm", "https://mail.google.com")
    addSampleData(db.db, bktName, "/ym", "https://mail.yahoo.com")
    pathToURLs := make(map[string]string)
    getData(db.db, bktName, pathToURLs)
    return MapHandler(pathToURLs, fallback)
}

func createBucket(db *bbolt.DB, bktName string) {
    db.Update(func(tx *bbolt.Tx) error {
        _, _ = tx.CreateBucketIfNotExists([]byte(bktName))
        return nil
    })
}

func addSampleData(db *bbolt.DB, bktName, key, value string) {
    db.Update(func(tx *bbolt.Tx) error {
        b := tx.Bucket([]byte(bktName))
        _ = b.Put([]byte(key), []byte(value))
        return nil
    })
}

func getData(db *bbolt.DB, bktName string, pathToURLs map[string]string) {
    db.View(func(tx *bbolt.Tx) error {
        b := tx.Bucket([]byte(bktName))
        c := b.Cursor()

        for k, v := c.First(); k != nil; k, v = c.Next() {
            pathToURLs[string(k)] = string(v)
        }
        return nil
    })
}

So while calling this handler:DefaultMap from main.go with below code

mux := http.NewServeMux()
var urlHandler http.HandlerFunc 
urlHandler = handler.DefaultMap(mux)
http.ListenAndServe(":8080", urlHandler)

But I am getting error as below

panic: runtime error: invalid memory address or nil pointer dereference
    panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x1b8 pc=0x6126cf]

goroutine 1 [running]:
go.etcd.io/bbolt.(*DB).Close(0x0)
panic({0x686180, 0x8dcb50})
    /usr/local/go/src/runtime/panic.go:1038 +0x215
go.etcd.io/bbolt.(*DB).beginRWTx(0x0)
    /home/xx/coding/go/packages/pkg/mod/go.etcd.io/bbolt@v1.3.6/db.go:634 +0x38
go.etcd.io/bbolt.(*DB).Begin(0x0, 0x0)
    /home/xx/coding/go/packages/pkg/mod/go.etcd.io/bbolt@v1.3.6/db.go:589 +0x1d
go.etcd.io/bbolt.(*DB).Update(0x0, 0xc000187e38)
    /home/xx/coding/go/packages/pkg/mod/go.etcd.io/bbolt@v1.3.6/db.go:725 +0x3e
github.com/xx/urlshortner/handler.createBucket(0x6c588d, {0x6c6358, 0xc000187ec8})
    /home/xx/coding/go/src/urlshortner/handler/handler.go:97 +0x48
github.com/xx/urlshortner/handler.DefaultMap({0x7243e0, 0xc00014a300})
    /home/xx/coding/go/src/urlshortner/handler/handler.go:88 +0x95
main.main()
    /home/xx/coding/go/src/urlshortner/main.go:24 +0xcc
exit status 2

Any help much appreciated.

Thank you.

Solution

This line:

github.com/xx/urlshortner/handler.createBucket(0x6c588d, {0x6c6358, 0xc000187ec8})

says error is in createBucket. Looking at createBucket:

    db.Update(func(tx *bbolt.Tx) error {

Based on the stack trace, it calls Update, and db is the only thing that can be nil here.

Tracing the call back, you can see:

func (u urlDB) ensureDB() {
    u.db, _ = bolt.Open("url.db", 0600, nil)
}

Thus, db open failed, and u.db is nil.

Answered By – Burak Serdar

Answer Checked By – Robin (GoLangFix Admin)

Leave a Reply

Your email address will not be published.