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

  1. Match field count to withSeconds: 5 fields when WithSeconds=false, 6 fields (with seconds first) when true
  2. Validate the expression with the same library locally (cron.ParseStandard or cron.NewParser with Second flag) before submitting
  3. 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)
  4. 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

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


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/60f4ea3b476e6da3. Report an issue: GitHub.