Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack overflow when calling value.Decode inside of structure #1000

Open
johnmarianhoffman opened this issue Oct 19, 2023 · 1 comment
Open

Comments

@johnmarianhoffman
Copy link

johnmarianhoffman commented Oct 19, 2023

Hi,

Looking for anyone's input on this. I don't think it's a bug necessarily (user error more than likely), but I'm doing what I understand to be the intended behavior based on documentation and the examples I can find.

I need a custom unmarshaler for my application. I'd like to use the yaml.Node.Decode method to partially/mostly unpack a Go structure. This should throw an error (but still unpack most fields) then do custom/special handling on the field that it chokes on. The problem is that I'm running into a stack overflow error with the decode method.

There is an "easy" if difficult-to-maintain solution to unpack into a map[string]interface{} first and then manually type-assert and copy each field over (seen a couple examples of this around the internet), however of ~10 fields total, 9 should work fine with the standard decoder, so this is a non-ideal solution.

The following toy example code reproduces my issue. This seems like a reasonably common use case, so please let me know if I've fundamentally misunderstood something about how this is supposed to work (or if the non-optimal, easy solution is the only solution for the time being.)

Thanks in advance!

package main

import (
	"gopkg.in/yaml.v3"	
	"log"
	"fmt"
)

type DataStruct struct {
	Val string
}

func (d *DataStruct) UnmarshalYAML(v *yaml.Node) error {

	// Decode as much as possible (should throw error!)
	err := v.Decode(d)

	// TODO: Verify error is what we expected and then handle special
	// unmarshaling
	
	return err
}

func main() {

	d := &DataStruct{}

	s := `val: sometestdata`

	err := yaml.Unmarshal([]byte(s), d)
	if err != nil {
		log.Fatal(err)
	}
	
	fmt.Println(d)
}

If you comment out the UnmarshalYAML method, this example works as intended, but does not give me the opporunity to "intercept" the handling of certain fields.

Here's the stack overflow error (minus all of the goroutine panics):

❯ go build && ./tmp
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0x140201f03a0 stack=[0x140201f0000, 0x140401f0000]
fatal error: stack overflow

runtime stack:
runtime.throw({0x102e16c78?, 0x102d664a8?})
	/usr/local/go/src/runtime/panic.go:1077 +0x40 fp=0x16d406d20 sp=0x16d406cf0 pc=0x102d6d930
runtime.newstack()
	/usr/local/go/src/runtime/stack.go:1107 +0x458 fp=0x16d406ed0 sp=0x16d406d20 pc=0x102d864c8
runtime.morestack()
	/usr/local/go/src/runtime/asm_arm64.s:316 +0x70 fp=0x16d406ed0 sp=0x16d406ed0 pc=0x102d99f60
@johnmarianhoffman
Copy link
Author

johnmarianhoffman commented Oct 19, 2023

FWIW, I also sort of get why this is failing, but also feel like wanting to leverage the default decoder for most fields within a structure while adding or modifying behavior for certain fields is a pretty common not entirely unreasonable use case, which makes me think that I've fundamentally misunderstood something about how the decoding workflow is supposed to happen, or maybe this should be feature? Happy to be told I'm way off base here. 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant