vitessio/vitess · error

symbol %s was not an interface but %T

Error message

symbol %s was not an interface but %T

What it means

After finding the symbol, extractSourceInterface verifies that a *types.Named symbol's underlying type is an interface. This error fires when the named symbol exists but resolves to a concrete type (struct, alias to struct, etc.), so the generator cannot extract a method set to implement.

Source

Thrown at go/vt/vtctl/vtctldclient/codegen/main.go:334

		}

		return nil, err
	}

	return pkg, nil
}

func extractSourceInterface(pkg *packages.Package, name string) (*types.Interface, error) {
	obj := pkg.Types.Scope().Lookup(name)
	if obj == nil {
		return nil, fmt.Errorf("no symbol found with name %s", name)
	}

	switch t := obj.Type().(type) {
	case *types.Named:
		iface, ok := t.Underlying().(*types.Interface)
		if !ok {
			return nil, fmt.Errorf("symbol %s was not an interface but %T", name, t.Underlying())
		}

		return iface, nil
	case *types.Interface:
		return t, nil
	}

	return nil, fmt.Errorf("symbol %s was not an interface but %T", name, obj.Type())
}

var vitessProtoRegexp = regexp.MustCompile(`^vitess\.io.*/proto/.*`)

func rewriteProtoImports(pkg *types.Package) string {
	if vitessProtoRegexp.MatchString(pkg.Path()) {
		return pkg.Name() + "pb"
	}

	return pkg.Name()

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Pass the interface type name, not the concrete implementation struct
  2. Inspect the type: `type X interface{...}` must exist in the package
  3. If the type was recently changed from interface to struct, update the codegen entrypoint to the new interface

Example fix

// before (struct passed)
codegen --source-interface VtctldServerImpl
// symbol VtctldServerImpl was not an interface but *types.Struct
// after
codegen --source-interface VtctldServer
Defensive patterns

Strategy: type-guard

Validate before calling

// Confirm it is declared as an interface
goDoc, _ := exec.Command("go", "doc", pkgPath, name).Output()
isIface := strings.Contains(string(goDoc), "interface {")

Type guard

func isNamedInterface(t types.Type) (*types.Named, bool) {
    n, ok := t.(*types.Named)
    if !ok { return nil, false }
    _, ok = n.Underlying().(*types.Interface)
    return n, ok
}

Prevention

When it happens

Trigger: Passing the name of a struct (e.g. the server implementation type) or other named non-interface type to extractSourceInterface instead of the interface type.

Common situations: Confusing the service interface with its implementation struct; a type that used to be an interface was refactored into a struct; passing a proto message type name by mistake.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/b8b5a7047cdf9919. Report an issue: GitHub.