vitessio/vitess · error

unreacheable

Error message

unreacheable

What it means

In evalCoerce, Char/VarChar coercions are handled earlier by an explicit evalToVarchar branch (which also swaps in the target collation). The later switch therefore treats a Char/VarChar target as unreachable and panics. Reaching it means control flow changed — e.g. the early branch was bypassed or a caller coerces to Char/VarChar through a path that no longer routes through the early return.

Source

Thrown at go/vt/vtgate/evalengine/eval.go:196

	}
	if col == collations.Unknown {
		panic("EvalResult.coerce with no collation")
	}
	if typ == sqltypes.VarChar || typ == sqltypes.Char {
		// if we have an explicit VARCHAR coercion, always force it so the collation is replaced in the target
		return evalToVarchar(e, col, false)
	}
	if e.SQLType() == typ && e.Size() == size && e.Scale() == scale {
		// nothing to be done here
		return e, nil
	}
	switch typ {
	case sqltypes.Null:
		return nil, nil
	case sqltypes.Binary, sqltypes.VarBinary:
		return evalToBinary(e), nil
	case sqltypes.Char, sqltypes.VarChar:
		panic("unreacheable")
	case sqltypes.Decimal:
		return evalToDecimal(e, 0, 0), nil
	case sqltypes.Float32, sqltypes.Float64:
		f, _ := evalToFloat(e)
		return f, nil
	case sqltypes.Int8, sqltypes.Int16, sqltypes.Int32, sqltypes.Int64:
		return evalToInt64(e), nil
	case sqltypes.Uint8, sqltypes.Uint16, sqltypes.Uint32, sqltypes.Uint64:
		return evalToInt64(e).toUint64(), nil
	case sqltypes.Date:
		return evalToDate(e, now, allowZero), nil
	case sqltypes.Datetime, sqltypes.Timestamp:
		return evalToDateTime(e, int(size), now, allowZero), nil
	case sqltypes.Time:
		return evalToTime(e, int(size)), nil
	default:
		return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Unsupported type conversion: %s", typ.String())
	}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Route the call through the explicit VARCHAR coercion path (evalToVarchar) instead of the generic switch
  2. If the early branch was refactored, restore handling for Char/VarChar in the switch by delegating to evalToVarchar(e, col, false)
  3. Add a regression test coercing to VarChar/Char to cover the path

Example fix

// before
case sqltypes.Char, sqltypes.VarChar:
	panic("unreacheable")
// after
case sqltypes.Char, sqltypes.VarChar:
	return evalToVarchar(e, col, false), nil
Defensive patterns

Strategy: type-guard

Validate before calling

// Go: only call evalCoerce's switch for non-CHAR/VARCHAR targets
if typ == sqltypes.Char || typ == sqltypes.VarChar {
	return evalToVarchar(e, col, false), nil
}

Type guard

func isCharTarget(typ sqltypes.Type) bool {
	return typ == sqltypes.Char || typ == sqltypes.VarChar
}

Try / catch

func safeCoerceToChar(e eval, col collations.ID) (out eval, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "char coercion failed: %v", r)
		}
	}()
	return evalToVarchar(e, col, false), nil
}

Prevention

When it happens

Trigger: Refactoring evalCoerce so the early `typ == VarChar || typ == Char` return no longer catches all string coercions (or a new call path passes sqltypes.Char/sqltypes.VarChar expecting the switch to handle it).

Common situations: Engine development: modifying coercion logic in eval.go or adding a coercion call site that forgot the explicit VARCHAR path; not reachable by end users on stock Vitess.

Related errors


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