vitessio/vitess · error

UnescapeID err: invalid input identifier '%s'

Error message

UnescapeID err: invalid input identifier '%s'

What it means

UnescapeID reverses backticking done by EscapeID. It throws for input that can never be a valid escaped identifier: an empty string or the literal '``' (empty quoted identifier), which the unescaper refuses to interpret.

Source

Thrown at go/sqlescape/ids.go:62

	}
	buf.WriteByte('`')
}

// EscapeIDs runs sqlescape.EscapeID() for all entries in the slice.
func EscapeIDs(identifiers []string) []string {
	result := make([]string, len(identifiers))
	for i := range identifiers {
		result[i] = EscapeID(identifiers[i])
	}
	return result
}

// UnescapeID reverses any backticking in the input string by EscapeID.
func UnescapeID(in string) (string, error) {
	l := len(in)

	if l == 0 || in == "``" {
		return "", fmt.Errorf("UnescapeID err: invalid input identifier '%s'", in)
	}

	if l == 1 {
		if in[0] == '`' {
			return "", errors.New("UnescapeID err: invalid input identifier '`'")
		}
		return in, nil
	}

	first, last := in[0], in[l-1]

	if first == '`' && last != '`' {
		return "", fmt.Errorf("UnescapeID err: unexpected single backtick at position %d in '%s'", 0, in)
	}
	if first != '`' && last == '`' {
		return "", fmt.Errorf("UnescapeID err: unexpected single backtick at position %d in '%s'", l, in)
	}
	if first != '`' && last != '`' {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Check for empty input before calling UnescapeID and return a clear caller-level error
  2. Fix the upstream code that produced an empty/empty-escaped identifier
  3. If empty identifiers are legitimate, handle them as a special case before unescaping

Example fix

// before
name, err := sqlescape.UnescapeID(id)
// after
if id == "" || id == "``" {
	return fmt.Errorf("identifier is empty")
}
name, err := sqlescape.UnescapeID(id)
Defensive patterns

Strategy: validation

Validate before calling

func safeUnescapeID(in string) (string, error) {
	if in == "" || in == "``" {
		return "", fmt.Errorf("empty identifier")
	}
	return sqlescape.UnescapeID(in)
}

Type guard

func isUnescapableID(in string) bool { return len(in) > 0 && in != "``" }

Try / catch

name, err := sqlescape.UnescapeID(id)
if err != nil {
	return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "bad identifier %q: %v", id, err)
}

Prevention

When it happens

Trigger: Calling UnescapeID("") or UnescapeID("``") — e.g. passing an empty table/column name, or an identifier that escaped to empty because the original name was empty.

Common situations: Empty identifiers coming from empty config values, missing metadata, or a caller that escaped an empty string and later unescapes it round-trip.

Related errors


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