errors.As fails for interpreted concrete error types due to reflect/type identity mismatch
Author: yrm006Created Mar 27, 2026Updated Mar 27, 2026
The following program sample.go triggers an unexpected result
package main
import (
"fmt"
"github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib"
)
func main() {
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
src := "" +
"import (\"errors\"; \"fmt\")\n" +
"type MyErr struct{}\n" +
"func (e *MyErr) Error() string { return \"x\" }\n" +
"func F() bool {\n" +
" err := fmt.Errorf(\"wrap: %w\", &MyErr{})\n" +
" var target *MyErr\n" +
" return errors.As(err, &target)\n" +
"}\n"
if _, err := i.Eval(src); err != nil {
panic(err)
}
v, err := i.Eval("F()")
fmt.Printf("v=%v err=%v\n", v, err)
}Expected result
errors.As should behave like compiled Go for interpreted error types.
In the repro:
- errors.As(fmt.Errorf("wrap: %w", &MyErr{}), &target)
should return true
- target should be set to the wrapped *MyErr
- no panic/error should occurGot
errors.As does not match compiled Go behavior.
In the repro:
- errors.As(fmt.Errorf("wrap: %w", &MyErr{}), &target)
does not return true
- instead evaluation fails with:
errors: *target must be interface or implement errorYaegi Version
v0.16.1
Additional Notes
This reproduces with pure yaegi (interp.New(...) + stdlib.Symbols) and does not depend on any host integration.
The issue appears to be in interpreted type / reflect identity handling rather than in %w unwrapping itself, because:
var err error = &MyErr{}workserrors.As(err, &targetErrorInterface)works- but
errors.As(err, &targetConcreteInterpretedType)fails
Related observation:
reflect.TypeOf((*MyErr)(nil)).Implements(error)evaluates to false in interpreted code- interpreted interface types appear as
interp.valueInterface
Source: traefik/yaegi