go-delve/delve · error

interface conversion: %s is %s, not %s

Error message

interface conversion: %s is %s, not %s

What it means

The mismatched-type variant of interface conversion failure: for 'x.(T)', if the assertion target type T (op.DwarfType) is non-nil and the dynamic type stored in the interface differs by name, evaluation fails with 'interface conversion: <iface name> is <dynamic type>, not <T>'. It mirrors Go's runtime panic but is produced at expression-evaluation time.

Source

Thrown at pkg/proc/eval.go:2149

		stack.err = fmt.Errorf("expression %q not an interface", astutil.ExprToString(op.Node.X))
		return
	}
	xv.loadInterface(0, false, LoadFullValue())
	if xv.Unreadable != nil {
		stack.err = xv.Unreadable
		return
	}
	if xv.Children[0].Unreadable != nil {
		stack.err = xv.Children[0].Unreadable
		return
	}
	if xv.Children[0].Addr == 0 {
		stack.err = fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), astutil.ExprToString(op.Node.Type))
		return
	}
	typ := op.DwarfType
	if typ != nil && xv.Children[0].DwarfType.Common().Name != typ.Common().Name {
		stack.err = fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.Common().Name, xv.Children[0].TypeString(), typ.Common().Name)
		return
	}
	// loadInterface will set OnlyAddr for the data member since here we are
	// passing false to loadData, however returning the variable with OnlyAddr
	// set here would be wrong since, once the expression evaluation
	// terminates, the value of this variable will be loaded.
	xv.Children[0].OnlyAddr = false
	stack.push(&xv.Children[0])
}

// Evaluates expressions <subexpr>[<subexpr>] (subscript access to arrays, slices and maps)
func (scope *EvalScope) evalIndex(op *evalop.Index, stack *evalStack) {
	idxev := stack.pop()
	xev := stack.pop()
	if xev.Unreadable != nil {
		stack.err = xev.Unreadable
		return
	}

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Print the interface first ('print x') to see the actual dynamic type, then assert that type.
  2. Unwrap errors before asserting: check err.(*fmt.wrapError).Unwrap() or use errors.As in the program.
  3. Use the correct concrete type in the assertion (pointer vs value matters: T vs *T).
  4. Replace the assertion in a condition with a safe check like 'x != nil' plus the right type.

Example fix

// before
dlv> print err.(MyErr)
// error: interface conversion: error is *fmt.wrapError, not main.MyErr
// after
dlv> print err.(*fmt.wrapError)   // or assert the actual dynamic type seen in 'print err'
Defensive patterns

Strategy: validation

Validate before calling

// Print the interface to learn its dynamic type before asserting:
// dlv> print x
// then assert exactly the printed concrete type (including *T vs T).

Try / catch

res, err := scope.EvalExpression(assertExpr, cfg)
if err != nil && strings.Contains(err.Error(), "interface conversion:") && strings.Contains(err.Error(), ", not ") {
    // dynamic type differs from asserted type; re-read err message for actual type
}

Prevention

When it happens

Trigger: Evaluating 'x.(T)' where the interface holds a concrete value of a different type than T — e.g. err.(MyErr) when err actually holds *errors.errorString or a different concrete type.

Common situations: Asserting the wrong concrete type when multiple types implement the same interface, asserting on error values that are wrapped (fmt.Errorf produces *fmt.wrapError, not MyErr), or version drift where the stored type changed.

Related errors


AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31). Data as JSON: /api/errors/844424532931fb85. Report an issue: GitHub.