vitessio/vitess · error

unsupported format specifier: %%%c

Error message

unsupported format specifier: %%%c

What it means

The strftime-style formatter in go/mysql/datetime encountered a `%` specifier whose conversion character is not present in the supported specifiers table (or maps to nil). Since the format string is user-supplied, the library refuses to silently emit a wrong value and returns an error describing the unsupported specifier.

Source

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

		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]})
		}
		p = p[2:]
	}
	return nil
}

// Format takes the format `p` and the time `t` to produce the
// format date/time. Note that this function re-compiles the
// pattern every time it is called.
//
// If you know beforehand that you will be reusing the pattern
// within your application, consider creating a `Strftime` object
// and reusing it.
func Format(p string, t DateTime, prec uint8) ([]byte, error) {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Replace the unsupported specifier with one from the supported set in go/mysql/datetime (e.g. use %s/%f/%T style equivalents or compute the piece in SQL).
  2. Escape the percent sign if a literal `%` followed by that character is intended, per the format's escaping rules.
  3. Pre-validate user-supplied format strings against the supported specifier list before passing them to the formatter.
  4. If the specifier is legitimately needed, add it to the `ds` specifiers map with a formatter implementation.

Example fix

// before
Format("%Y-%m-%d %K") // %K unsupported

// after
Format("%Y-%m-%d %H:%i:%s")
Defensive patterns

Strategy: validation

Validate before calling

var supported = map[byte]bool{'Y': true, 'm': true, 'd': true, 'H': true, 'i': true, 's': true /* full set from datetime package */}
func validFormat(f string) bool {
    for i := 0; i < len(f); i++ {
        if f[i] == '%' {
            if i+1 >= len(f) || !supported[f[i+1]] {
                return false
            }
            i++
        }
    }
    return true
}

Try / catch

out, err := dt.Format(layout, t)
if err != nil {
    if strings.Contains(err.Error(), "unsupported format specifier") {
        out, err = dt.Format(fallbackLayout, t)
    }
}

Prevention

When it happens

Trigger: Calling datetime Format/New (compile step) with a format string containing `%%c` where the character after `%` is not one of the supported strftime specifiers — e.g. `%K`, `%J`, or a literal `%` followed by a non-specifier letter.

Common situations: Users porting DATE_FORMAT/STRFTIME format strings from other databases or languages (Python strftime, C strftime) that support specifiers this implementation does not; typos in format templates stored in application config; locale-specific formats containing unsupported directives.

Related errors


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