{"record":{"id":"50d1bd8962f43363","repo":"XTLS/Xray-core","slug":"invalid-geodata-cron","errorCode":null,"errorMessage":"invalid geodata cron","messagePattern":"invalid geodata cron","errorType":"validation","errorClass":"errors.Error","httpStatus":null,"severity":"error","filePath":"app/geodata/geodata.go","lineNumber":48,"sourceCode":"\t\tassets: config.Assets,\n\t}\n\n\tif len(g.assets) > 0 {\n\t\tvar dispatcher routing.Dispatcher\n\t\tif err := core.RequireFeatures(ctx, func(d routing.Dispatcher) {\n\t\t\tdispatcher = d\n\t\t}); err != nil {\n\t\t\treturn nil, errors.New(\"failed to get dispatcher for geodata downloader\").Base(err)\n\t\t}\n\t\tg.downloader = newDownloader(ctx, dispatcher, config.Outbound)\n\t}\n\n\tg.tasker = cron.New(\n\t\tcron.WithChain(cron.SkipIfStillRunning(cron.DiscardLogger)),\n\t\tcron.WithLogger(cron.DiscardLogger),\n\t)\n\tif _, err := g.tasker.AddFunc(config.Cron, g.execute); err != nil {\n\t\treturn nil, errors.New(\"invalid geodata cron\").Base(err)\n\t}\n\terrors.LogInfo(ctx, \"scheduled geodata reload with cron: \", config.Cron)\n\n\treturn g, nil\n}\n\nfunc (g *Instance) execute() {\n\tvar err error\n\tif g.downloader != nil {\n\t\terr = g.reloadWithUpdate()\n\t} else {\n\t\terr = reload()\n\t}\n\tif err != nil {\n\t\terrors.LogErrorInner(context.Background(), err, \"scheduled geodata reload failed\")\n\t}\n}\n","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/geodata/geodata.go#L30-L66","documentation":"Returned when the robfig/cron parser rejects the configured geodata update schedule (config.Cron) passed to g.tasker.AddFunc. The cron library's parse error is chained as the Base cause.","triggerScenarios":"Setting geodata.updateCron/cron in the Xray config to a string that is not a valid cron expression — wrong field count, bad ranges like '70 * * * *', unsupported macros, or stray characters.","commonSituations":"Using 6-field or 7-field cron strings when the parser expects 5; typos such as '*/2 hourse * * *'; assuming systemd-timer or crontab shorthand syntax that robfig/cron v3 does not accept by default.","solutions":["Use a standard 5-field cron expression, e.g. '30 4 * * *' for 04:30 daily","Read the chained Base error from the cron parser — it pinpoints the offending field","Validate the expression with an online cron parser or robfig/cron's parser in a scratch program before shipping config"],"exampleFix":"// before (xray config json)\n\"geodata\": { \"cron\": \"every 2 hours\" } // invalid\n// after\n\"geodata\": { \"cron\": \"0 */2 * * *\" } // valid 5-field cron","handlingStrategy":"validation","validationCode":"// Validate the cron expression with the same parser the app uses:\nimport \"github.com/robfig/cron/v3\"\n\nparser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)\nif _, err := parser.Parse(config.Cron); err != nil {\n    return fmt.Errorf(\"bad geodata cron %q: %w\", config.Cron, err)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use 5-field standard cron syntax for geodata updates","Validate config templates with a schema/lint step before deployment","Read the chained Base error to find the offending field"],"tags":["config","cron","geodata","scheduling"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}