vitessio/vitess · error

stray %% at the end of pattern

Error message

stray %% at the end of pattern

What it means

The strftime-style pattern compiler in go/mysql/datetime rejects format strings that end with a bare '%' — the trailing '%' has no following conversion specifier byte, so the compiled program would be incomplete. Returned by compile (invoked via New and Format) when strings.IndexByte finds '%' at the last position of the pattern.

Source

Thrown at go/mysql/datetime/strftime.go:34

*/

package datetime

import (
	"errors"
	"fmt"
	"strings"
)

func compile(ds map[byte]Spec, p string, exec func(Spec)) error {
	for l := len(p); l > 0; l = len(p) {
		i := strings.IndexByte(p, '%')
		if i < 0 {
			exec(&fmtVerbatim{s: p})
			break
		}
		if i == l-1 {
			return errors.New(`stray %% at the end of pattern`)
		}

		// we found a '%'. we need the next byte to decide what to do next
		// we already know that i < l - 1
		// everything up to the i is verbatim
		if i > 0 {
			exec(&fmtVerbatim{s: p[:i]})
			p = p[i:]
		}

		if spec, ok := ds[p[1]]; ok {
			if spec == nil {
				return fmt.Errorf(`unsupported format specifier: %%%c`, p[1])
			}
			exec(spec)
		} else {
			exec(&fmtVerbatim{s: p[1:2]})
		}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Remove the trailing '%' or add the intended specifier after it (e.g. '%%' for a literal percent sign)
  2. Double literal percent signs ('%%') in the pattern, since % introduces a specifier
  3. Trim or sanitize user-supplied format templates before compiling

Example fix

// before
out, err := datetime.Format("%Y-%m-%d %", ts)
// after
out, err := datetime.Format("%Y-%m-%d %%", ts)
Defensive patterns

Strategy: validation

Validate before calling

func validStrftimePattern(p string) bool { return p == "" || !strings.HasSuffix(p, "%") }

Type guard

func safeFormatPattern(p string) (string, bool) {
    if strings.HasSuffix(p, "%") { return strings.TrimSuffix(p, "%"), false }
    return p, true
}

Try / catch

out, err := datetime.Format(pattern, ts)
if err != nil && strings.Contains(err.Error(), "stray %") {
    out, err = datetime.Format(strings.TrimRight(pattern, "%"), ts)
}

Prevention

When it happens

Trigger: Format(pattern, t) or New(pattern) with a pattern whose last character is '%', e.g. "%Y-%m-%d %".

Common situations: Format strings assembled by string concatenation where a literal percent got orphaned; translating a format from another language and forgetting '%' must be escaped by doubling; copy-pasted templates with trailing whitespace stripped.

Related errors


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