flipped-aurora/gin-vue-admin · error
cron 表达式非法: %w
Error message
cron 表达式非法: %w
What it means
ValidateSpec checks a cron expression before persisting a timed task. It parses with a 6-field parser (seconds enabled) or cron.ParseStandard (5 fields) depending on withSeconds; a parse failure is wrapped as 'cron 表达式非法'. This follows robfig/cron semantics: field count and descriptors must match the chosen parser mode.
Source
Thrown at server/service/system/sys_timed_task.go:39
// TimedTaskService 定时任务服务(全局单例 + global.GVA_DB, 遵项目既有约定)
type TimedTaskService struct{}
var TimedTaskServiceApp = new(TimedTaskService)
// timedTaskCronName 一任务一 cronName: robfig/cron 无单任务 pause,
// 启停语义 = Clear(cronName) + 按 DB 重加, 状态以 DB enabled 为准。
func timedTaskCronName(id uint) string { return fmt.Sprintf("timedTask/%d", id) }
// ValidateSpec 服务端校验 cron 表达式(含 @daily/@hourly 等描述符)
func (s *TimedTaskService) ValidateSpec(spec string, withSeconds bool) error {
var err error
if withSeconds {
_, err = cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor).Parse(spec)
} else {
_, err = cron.ParseStandard(spec)
}
if err != nil {
return fmt.Errorf("cron 表达式非法: %w", err)
}
return nil
}
// validateTask 落库前统一校验(创建/更新共用)
func (s *TimedTaskService) validateTask(t *system.SysTimedTask) error {
if t.Name == "" {
return errors.New("任务名不能为空")
}
if err := s.ValidateSpec(t.Spec, t.WithSeconds); err != nil {
return err
}
switch t.ExecutorType {
case system.TimedTaskExecutorMethod:
if _, ok := task.Get(t.MethodName); !ok {
return fmt.Errorf("方法 %s 未注册", t.MethodName)
}
if len(t.Params) > 0 && !json.Valid(t.Params) {View on GitHub (pinned to 3136500ef3)
Solutions
- Match field count to withSeconds: 5 fields when WithSeconds=false, 6 fields (with seconds first) when true
- Validate the expression with the same library locally (cron.ParseStandard or cron.NewParser with Second flag) before submitting
- Fix out-of-range values in any field (minute 0-59, hour 0-23, day-of-month 1-31, month 1-12, day-of-week 0-6)
- Use descriptors like @daily/@hourly only if the selected parser supports them
Example fix
// before
{ "spec": "0 30 2 * * *", "withSeconds": false } // 6 fields, standard parser -> invalid
// after
{ "spec": "30 2 * * *", "withSeconds": false }
// or
{ "spec": "0 30 2 * * *", "withSeconds": true } Defensive patterns
Strategy: validation
Validate before calling
// Go: pre-validate with the same parsers used by ValidateSpec
func validCron(spec string, withSeconds bool) bool {
if withSeconds {
p := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
_, err := p.Parse(spec)
return err == nil
}
_, err := cron.ParseStandard(spec)
return err == nil
} Try / catch
err := svc.CreateTimedTask(task)
if err != nil && strings.Contains(err.Error(), "cron 表达式非法") {
// fix the spec: 5 fields (standard) or 6 fields with seconds, then resubmit
return fmt.Errorf("请检查 cron 字段数与取值范围: %w", err)
} Prevention
- Use 5 fields for standard mode, 6 (seconds first) only when withSeconds=true
- Test cron strings in the same library (robfig/cron) before submitting
- Watch for quartz-style expressions silently pasted into a standard parser
- Prefer named descriptors (@daily, @hourly) for simple schedules
When it happens
Trigger: Creating or updating a timed task whose Spec cannot be parsed: wrong number of fields for the withSeconds mode (e.g. 5 fields with withSeconds=true, or 6 fields with withSeconds=false), invalid field values (e.g. month 13), or misused descriptors.
Common situations: Pasting a 6-field quartz-style expression into a task configured with WithSeconds=false; using ranges like '0 0 * * * *' with standard 5-field parsing; typos such as '* * *' (missing fields); interval descriptors incompatible with the parser.
Related errors
- 参数错误:executionPlan 必须提供
- packageName 不能为空
- packageType 必须是 'package' 或 'plugin'
- packageType 和 packageInfo.template 必须保持一致
- 当 needCreatedPackage=true 时,packageInfo 不能为空
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/60f4ea3b476e6da3.
Report an issue: GitHub.