vitessio/vitess · error
interface %s implemented by %s (%s as %T) without ptr
Error message
interface %s implemented by %s (%s as %T) without ptr
What it means
asthelpergen walks all types that implement a target interface and, when onlyReferences is set, requires every implementing type to be a pointer or an interface (reference types). This error is thrown when a value (non-pointer) concrete type implements the interface, which the generators cannot emit helpers for. The message names the interface, the implementing type, and its underlying kind.
Source
Thrown at go/tools/asthelpergen/asthelpergen.go:175
}
}
func findImplementations(scope *types.Scope, iff *types.Interface, impl func(types.Type) error) error {
const onlyReferences = false
for _, name := range scope.Names() {
obj := scope.Lookup(name)
if _, ok := obj.(*types.TypeName); !ok {
continue
}
baseType := obj.Type()
if types.Implements(baseType, iff) {
if onlyReferences {
switch tt := baseType.Underlying().(type) {
case *types.Interface:
// This is OK; interfaces are references
default:
return fmt.Errorf("interface %s implemented by %s (%s as %T) without ptr", iff.String(), baseType, tt.String(), tt)
}
}
if types.TypeString(baseType, noQualifier) == visitableName {
// skip the visitable interface
continue
}
if err := impl(baseType); err != nil {
return err
}
continue
}
pointerT := types.NewPointer(baseType)
if types.Implements(pointerT, iff) {
if err := impl(pointerT); err != nil {
return err
}
continue
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Change the implementing type's methods to pointer receivers and use *T wherever the interface is required
- Remove the value-type implementer from the package set passed via options.Packages if it should not be generated
- If references are not actually required, set onlyReferences=false in the generator Options
Example fix
// before
func (n Ident) SQLNode() {}
// after
func (n *Ident) SQLNode() {} Defensive patterns
Strategy: validation
Validate before calling
types := collector.Implementations(interfaceName)
for _, t := range types {
if _, isPtr := t.(*types.Pointer); !isPtr {
if _, isIface := t.Underlying().(*types.Interface); !isIface {
return fmt.Errorf("value type %s implements %s; use pointer receiver", t, interfaceName)
}
}
} Type guard
func isReferenceType(t types.Type) bool {
switch t.Underlying().(type) {
case *types.Interface, *types.Pointer:
return true
}
return false
} Prevention
- Always define AST node methods with pointer receivers
- Audit implementers of an interface (golang.org/x/tools `implements` or gopls) before generating helpers
- Run asthelpergen in CI so violations are caught on the PR
When it happens
Trigger: Running GenerateASTHelpers for an interface where a struct (or other non-reference underlying type) implements the interface by value while onlyReferences=true is configured, e.g. `type MyStruct struct{...}` with value-receiver methods satisfying e.g. SQLNode.
Common situations: A developer adds a new AST type with value receivers; refactoring moves methods from pointer receivers to value receivers; generating helpers for a new interface without auditing all implementers.
Related errors
- unexpected input type: %s
- unknown type %T %v
- must specify exactly one package
- package '%s' does not contain 'ast_format.go'
- missing file on disk: %s (%w)
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/d2a45a63a9bc3ae7.
Report an issue: GitHub.