runtime: goroutine stack exceeds 1000000000-byte limit, fatal error: stack overflow on printing a nested struct

12,169

The %v and %+v formats use the value of String() if the type implements it. Therefore, using %+v on a type within the String() function for that type causes infinite recursion. Instead of using %+v in the String() function, you'll have to construct your own string, showing the contents of the structure in whatever way you see fit.

Share:
12,169
nohup
Author by

nohup

Writes bad code occasionally.

Updated on June 05, 2022

Comments

  • nohup
    nohup almost 2 years

    I have a nested struct.

    type ConfigOne struct {
        // Daemon section from config file.
        Daemon daemon
    }
    type daemon struct {
        Loglevel int
        Logfile string
    }
    

    And I have a String() string method on that type, which I am trying to return the nested struct elements as

    func (c ConfigOne)String()  string{
        return fmt.Sprintf("%+v\n", c)
    }
    

    When I am trying to print it as

    c := &modules.ConfigOne{}
    c.Daemon.Loglevel = 1
    c.Daemon.Logfile = "/tmp/test.log"
    modules.Logger.Infoln(c.String())
    

    I am getting the error

    runtime: goroutine stack exceeds 1000000000-byte limit fatal error: stack overflow

    runtime stack: runtime.throw(0x6ea3b7, 0xe) ...

    After going through the error, I could see repeated lines similar to the below one

    modules/structs.go:31 +0x77 fp=0xc440100398 sp=0xc440100328 go-consume/modules.(*ConfigOne).String(0xc42abcb4e0, 0x70bc08, 0xc42abd6300) :1 +0x64 fp=0xc4401003d8 sp=0xc440100398 fmt.(*pp).handleMethods(0xc42abd6300, 0xc400000076, 0x410301)

    and finally, before dying,

    modules/structs.go:31 +0xc0 fp=0xc440103d18 sp=0xc440103ca8 ...additional frames elided...

    goroutine 17 [syscall, locked to thread]: runtime.goexit()

    which I believe is caused by going into some infinite recursion.

    I tried my luck to find the cause and reached here, which I believe is the same issue. However I couldn't understand the explanation in that thread.

    If I try to print the individual nested struct as

    func (c ConfigOne)String() string{
        //return fmt.Sprintf("%+v\n", c.Daemon.Loglevel)
        return fmt.Sprintf("%+v\n", c.Daemon)
    }
    

    it is working fine, and log shows the fields as

    2017/03/05 01:28:25 go-consume.go:38: INFO: {Loglevel:1 Logfile:/tmp/test.log}
    

    Can someone kindly explain how the former String() method is resulting in an infinite recursion and a stackoverflow, and what is the best way to overcome this?

  • nohup
    nohup about 7 years
    Thank you @andy-schweig.