vitessio/vitess · error

called EvalResult.truncate on non-quoted

Error message

called EvalResult.truncate on non-quoted

What it means

evalBytes.truncateInPlace truncates a string/binary value to a given size. Text and binary values can be byte/charset-aware truncated, but other flavors of evalBytes (e.g. JSON or enum-backed strings stored as evalBytes) have no defined truncation, so the default case panics. It signals the truncate call was made on a value kind that doesn't support quoted/truncation semantics.

Source

Thrown at go/vt/vtgate/evalengine/eval_bytes.go:181

func (e *evalBytes) withCollation(col collations.TypedCollation) *evalBytes {
	return newEvalRaw(e.SQLType(), e.bytes, col)
}

func (e *evalBytes) truncateInPlace(size int) {
	switch tt := e.SQLType(); {
	case sqltypes.IsBinary(tt):
		if size > len(e.bytes) {
			pad := make([]byte, size)
			copy(pad, e.bytes)
			e.bytes = pad
		} else {
			e.bytes = e.bytes[:size]
		}
	case sqltypes.IsText(tt):
		collation := colldata.Lookup(e.col.Collation)
		e.bytes = charset.Slice(collation.Charset(), e.bytes, 0, size)
	default:
		panic("called EvalResult.truncate on non-quoted")
	}
}

func (e *evalBytes) toDateBestEffort() datetime.DateTime {
	if t, _, _ := datetime.ParseDateTime(e.string(), -1); !t.IsZero() {
		return t
	}
	if t, _ := datetime.ParseDate(e.string()); !t.IsZero() {
		return datetime.DateTime{Date: t}
	}
	return datetime.DateTime{}
}

func (e *evalBytes) parseNumericBytes(number *[8]byte) bool {
	raw := e.bytes
	if l := len(raw); l > 8 {
		for _, b := range raw[:l-8] {
			if b != 0 {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Check e.SQLType() before calling truncateInPlace and skip or convert non-text/binary values
  2. Add a case in truncateInPlace for the new evalBytes flavor if truncation is meaningful for it
  3. File a Vitess issue with the crashing CAST/coercion expression if it reproduces on unmodified code

Example fix

// before
if e.SQLType() == sqltypes.VarChar {
	e.truncateInPlace(size, col)
}
// after
tt := e.SQLType()
if sqltypes.IsText(tt) || sqltypes.IsBinary(tt) {
	e.truncateInPlace(size, col)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Go: check the type is truncatable before calling
tt := e.SQLType()
if !sqltypes.IsText(tt) && !sqltypes.IsBinary(tt) {
	return // skip truncation or convert first
}

Type guard

func isTruncatable(e *evalBytes) bool {
	tt := e.SQLType()
	return sqltypes.IsText(tt) || sqltypes.IsBinary(tt)
}

Try / catch

func safeTruncate(e *evalBytes, size int32, col collations.ID) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "truncate failed: %v", r)
		}
	}()
	e.truncateInPlace(size, col)
	return nil
}

Prevention

When it happens

Trigger: Coercion/casting machinery calling truncateInPlace on an evalBytes whose SQLType is neither binary nor text — e.g. after a type was widened/changed upstream (say a JSON value held in evalBytes) and the caller didn't re-check the type before truncating.

Common situations: Engine development: adding new cast targets or changing how values are stored in evalBytes; end users may see this as a vtgate crash on a specific CAST/coercion query.

Related errors


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