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
- 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).
- Escape the percent sign if a literal `%` followed by that character is intended, per the format's escaping rules.
- Pre-validate user-supplied format strings against the supported specifier list before passing them to the formatter.
- 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
- Keep format templates in a validated constant set, not free user input.
- When porting formats from other languages, map each specifier to the supported set first.
- Add a unit test per format template used in production.
- Escape literal percent signs per the format's rules.
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
- stray %% at the end of pattern
- overflow
- unexpected character %q
- can't convert %q to decimal: too short
- can't convert %s to decimal: %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/f08ab40ba50ffda7.
Report an issue: GitHub.