larksuite/cli · error
date %q must be ISO yyyy-mm-dd: %w
Error message
date %q must be ISO yyyy-mm-dd: %w
What it means
For values containing a 'T' (ISO datetime), isoDateToSerial first parses the date portion with layout 2006-01-02. If that base date is malformed (e.g. "2026-13-01T..." or "01/31/2026T..."), the underlying time.Parse error is wrapped in this message. Callers add row/column context in the typed validation error.
Source
Thrown at shortcuts/sheets/lark_sheet_table_io.go:781
// `T` separator (`2024-01-15T00:00:00.000`, `2024-01-15T08:30:00+08:00`). The
// `T...` suffix is dropped before parsing so the pandas `df_to_sheet` helper
// — which uses `df.to_json(orient="split", date_format="iso")` and therefore
// always emits the full ISO form — round-trips without an extra string clean
// step on the agent side. A leading `T` (no date prefix) is left alone so the
// parser still rejects it cleanly.
func isoDateToSerial(s string) (float64, error) {
s = strings.TrimSpace(s)
if s == "" {
// Empty cells in a date-typed column are the classic header/total-row
// clash with the column-wide dtype declaration; name the three ways
// out so the caller does not have to guess what "bad format" means.
return 0, fmt.Errorf("date column has an empty cell — drop the empty rows, fill real yyyy-mm-dd dates, or declare the column dtype as object (text)") //nolint:forbidigo // intermediate error; callers wrap it into a typed --sheets/--values validation error with row/column context
}
if i := strings.Index(s, "T"); i > 0 {
base := s[:i]
date, err := time.Parse("2006-01-02", base)
if err != nil {
return 0, fmt.Errorf("date %q must be ISO yyyy-mm-dd: %w", base, err) //nolint:forbidigo // intermediate parse error; caller wraps it with typed validation context
}
var parsed time.Time
clock := s[i:]
if strings.ContainsAny(clock, "Zz+-") {
parsed, err = time.Parse(time.RFC3339Nano, s)
} else {
parsed, err = time.Parse("2006-01-02T15:04:05.999999999", s)
}
if err != nil {
return 0, fmt.Errorf("datetime %q must be ISO: %w", s, err) //nolint:forbidigo // intermediate parse error; caller wraps it with typed validation context
}
if parsed.Location() != time.UTC {
// Keep the wall-clock time supplied by the caller; Excel serials do
// not carry a timezone and table-put historically treated the ISO
// date/time as the sheet-local value.
return date.Sub(excelEpoch).Hours()/24 + float64(parsed.Hour()*3600+parsed.Minute()*60+parsed.Second())/86400 + float64(parsed.Nanosecond())/(86400*1e9), nil
}
return date.Sub(excelEpoch).Hours()/24 + float64(parsed.Hour()*3600+parsed.Minute()*60+parsed.Second())/86400 + float64(parsed.Nanosecond())/(86400*1e9), nilView on GitHub (pinned to 7fd6ef3c07)
Solutions
- Reformat the value to strict yyyy-mm-dd before the T, e.g. "2026-01-31T10:00:00"
- Zero-pad month and day (01-05 not 1-5)
- If the column holds non-ISO text, change its dtype to "object"
Example fix
// before
{"name":"Due","type":"date","data":["31/01/2026T10:00"]}
// after
{"name":"Due","type":"date","data":["2026-01-31T10:00:00"]} Defensive patterns
Strategy: validation
Validate before calling
function assertISOBasedate(v) {
const i = v.indexOf('T');
if (i > 0 && isNaN(Date.parse(v.slice(0, i) + 'T00:00:00Z')))
throw new Error(`base date '${v.slice(0, i)}' is not yyyy-mm-dd`);
} Type guard
function hasValidISOBaseDate(v) { const m = /^(\d{4}-\d{2}-\d{2})T/.exec(v); return !!m && !isNaN(Date.parse(m[1] + 'T00:00:00Z')); } Try / catch
try {
await run(['lark','sheet','table-put','--sheets',payload]);
} catch (e) {
if (/must be ISO yyyy-mm-dd/.test(e.message)) {
// reformat the pre-T part to yyyy-mm-dd and retry
}
throw e;
} Prevention
- Always zero-pad month/day in dates (2026-01-05, not 2026-1-5)
- Convert locale-formatted dates (MM/DD/YYYY, DD.MM.YYYY) to ISO before payload build
- Validate with a strict regex ^\d{4}-\d{2}-\d{2} before sending
When it happens
Trigger: Date column value like "31/01/2026T10:00", "2026-1-5T09:00:00", or "2026-13-01T00:00" — the part before 'T' is not a valid yyyy-mm-dd date.
Common situations: US-style or locale-formatted dates pasted from other tools; single-digit month/day without zero padding; upstream systems emitting local formats.
Related errors
- datetime %q must be ISO: %w
- date expects an ISO yyyy-mm-dd string, got %s
- date column has an empty cell — drop the empty rows, fill re
- please select at least one domain
- %s must contain at most %d characters
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/03b644dd35704455.
Report an issue: GitHub.