Gorm: runtime error: invalid memory address or nil pointer dereference on simple learning example. Maybe a bug with latest version?

Created on 11 Jan 2018  路  1Comment  路  Source: go-gorm/gorm

What version of Go are you using (go version)?

go version go1.9.2 windows/amd64

Which database and its version are you using?

Postgres, 9 and 10

What did you do?

I use this code below, found here: https://medium.com/@thedevsaddam/build-restful-api-service-in-golang-using-gin-gonic-framework-85b1a6e176f3

When I launch it with go run main.go and try to call the v1.GET("/", fetchAllTodo) API it throw like this:

[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /api/v1/todos/            --> main.createTodo (3 handlers)
[GIN-debug] GET    /api/v1/todos/            --> main.fetchAllTodo (3 handlers)
[GIN-debug] GET    /api/v1/todos/:id         --> main.fetchSingleTodo (3 handlers)
[GIN-debug] PUT    /api/v1/todos/:id         --> main.updateTodo (3 handlers)
[GIN-debug] DELETE /api/v1/todos/:id         --> main.deleteTodo (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2018/01/11 - 00:56:46 | 404 |            0s |             ::1 | GET      /api/v1
[GIN-debug] redirecting request 301: /api/v1/todos --> /api/v1/todos/


2018/01/11 00:56:50 [Recovery] panic recovered:
GET /api/v1/todos/ HTTP/1.1
Host: localhost:8080
Accept: */*


runtime error: invalid memory address or nil pointer dereference
C:/Go/src/runtime/panic.go:491 (0x42bff0)
        gopanic: reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
C:/Go/src/runtime/panic.go:63 (0x42ae74)
        panicmem: panic(memoryError)
C:/Go/src/runtime/signal_windows.go:161 (0x440302)
        sigpanic: panicmem()
C:/Users/John/go/src/github.com/jinzhu/gorm/main.go:715 (0x942c7e)
        (*DB).clone: db:                s.db,
C:/Users/John/go/src/github.com/jinzhu/gorm/main.go:293 (0x93f1b5)
        (*DB).Find: return s.clone().NewScope(out).inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
C:/projects/gotest/main.go:71 (0x9884f1)
        fetchAllTodo: db.Find(&todos)
C:/Users/John/go/src/github.com/gin-gonic/gin/context.go:107 (0x8efcb9)
        (*Context).Next: c.handlers[c.index](c)
C:/Users/John/go/src/github.com/gin-gonic/gin/recovery.go:46 (0x9024c0)
        RecoveryWithWriter.func1: c.Next()
C:/Users/John/go/src/github.com/gin-gonic/gin/context.go:107 (0x8efcb9)
        (*Context).Next: c.handlers[c.index](c)
C:/Users/John/go/src/github.com/gin-gonic/gin/logger.go:83 (0x9017e2)
        LoggerWithWriter.func1: c.Next()
C:/Users/John/go/src/github.com/gin-gonic/gin/context.go:107 (0x8efcb9)
        (*Context).Next: c.handlers[c.index](c)
C:/Users/John/go/src/github.com/gin-gonic/gin/gin.go:359 (0x8f912c)
        (*Engine).handleHTTPRequest: c.Next()
C:/Users/John/go/src/github.com/gin-gonic/gin/gin.go:326 (0x8f88b1)
        (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
C:/Go/src/net/http/server.go:2619 (0x66bd6a)
        serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
C:/Go/src/net/http/server.go:1801 (0x667d93)
        (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
C:/Go/src/runtime/asm_amd64.s:2337 (0x457ee0)
        goexit: BYTE    $0x90   // NOP

[GIN] 2018/01/11 - 00:56:50 | 500 |      10.005ms |             ::1 | GET      /api/v1/todos/
exit status 2
package main

import (
    "net/http"
    "strconv"

    "github.com/gin-gonic/gin"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/postgres"
)

var db *gorm.DB

func init() {
    //open a db connection
    var err error
    db, err := gorm.Open("postgres", "host=localhost user=postgres dbname=postgres sslmode=disable password=pass")
    if err != nil {
        panic("failed to connect database")
    }

    //Migrate the schema
    db.AutoMigrate(&todoModel{})
}

func main() {

    router := gin.Default()

    v1 := router.Group("/api/v1/todos")
    {
        v1.POST("/", createTodo)
        v1.GET("/", fetchAllTodo)
        v1.GET("/:id", fetchSingleTodo)
        v1.PUT("/:id", updateTodo)
        v1.DELETE("/:id", deleteTodo)
    }
    router.Run()

}

type (
    // todoModel describes a todoModel type
    todoModel struct {
        gorm.Model
        Title     string `json:"title"`
        Completed int    `json:"completed"`
    }

    // transformedTodo represents a formatted todo
    transformedTodo struct {
        ID        uint   `json:"id"`
        Title     string `json:"title"`
        Completed bool   `json:"completed"`
    }
)

// createTodo add a new todo
func createTodo(c *gin.Context) {
    completed, _ := strconv.Atoi(c.PostForm("completed"))
    todo := todoModel{Title: c.PostForm("title"), Completed: completed}
    db.Save(&todo)
    c.JSON(http.StatusCreated, gin.H{"status": http.StatusCreated, "message": "Todo item created successfully!", "resourceId": todo.ID})
}

// fetchAllTodo fetch all todos
func fetchAllTodo(c *gin.Context) {
    var todos []todoModel
    var _todos []transformedTodo

    db.Find(&todos)

    if len(todos) <= 0 {
        c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "No todo found!"})
        return
    }

    //transforms the todos for building a good response
    for _, item := range todos {
        completed := false
        if item.Completed == 1 {
            completed = true
        } else {
            completed = false
        }
        _todos = append(_todos, transformedTodo{ID: item.ID, Title: item.Title, Completed: completed})
    }
    c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": _todos})
}

// fetchSingleTodo fetch a single todo
func fetchSingleTodo(c *gin.Context) {
    var todo todoModel
    todoID := c.Param("id")

    db.First(&todo, todoID)

    if todo.ID == 0 {
        c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "No todo found!"})
        return
    }

    completed := false
    if todo.Completed == 1 {
        completed = true
    } else {
        completed = false
    }

    _todo := transformedTodo{ID: todo.ID, Title: todo.Title, Completed: completed}
    c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": _todo})
}

// updateTodo update a todo
func updateTodo(c *gin.Context) {
    var todo todoModel
    todoID := c.Param("id")

    db.First(&todo, todoID)

    if todo.ID == 0 {
        c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "No todo found!"})
        return
    }

    db.Model(&todo).Update("title", c.PostForm("title"))
    completed, _ := strconv.Atoi(c.PostForm("completed"))
    db.Model(&todo).Update("completed", completed)
    c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "message": "Todo updated successfully!"})
}

// deleteTodo remove a todo
func deleteTodo(c *gin.Context) {
    var todo todoModel
    todoID := c.Param("id")

    db.First(&todo, todoID)

    if todo.ID == 0 {
        c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "No todo found!"})
        return
    }

    db.Delete(&todo)
    c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "message": "Todo deleted successfully!"})
}

Most helpful comment

The problem was the line:

db, err := gorm.Open...

instead of:

db, err = gorm.Open...

So... the assignment.

>All comments

The problem was the line:

db, err := gorm.Open...

instead of:

db, err = gorm.Open...

So... the assignment.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

youtwo123 picture youtwo123  路  3Comments

hypertornado picture hypertornado  路  3Comments

satb picture satb  路  3Comments

kumarsiva07 picture kumarsiva07  路  3Comments

rfyiamcool picture rfyiamcool  路  3Comments