{"record":{"id":"8a172fa9649ec883","repo":"jlcodes99/cockpit-tools","slug":"crontab-field-empty","errorCode":"crontab_field_empty","errorMessage":"crontab_field_empty","messagePattern":"crontab_field_empty","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/pages/WakeupTasksPage.tsx","lineNumber":520,"sourceCode":"\n  const single = parseCrontabNumber(rangePart);\n  validateCrontabValue(single, min, max, normalizeDayOfWeek);\n  if (step === 1) {\n    target.add(normalizeDayOfWeek ? normalizeCrontabDayOfWeek(single) : single);\n    return;\n  }\n  insertCrontabRange(target, single, max, step, normalizeDayOfWeek);\n};\n\nconst parseCrontabField = (\n  field: string,\n  min: number,\n  max: number,\n  normalizeDayOfWeek: boolean,\n): ParsedCronField => {\n  const trimmed = field.trim();\n  if (!trimmed) {\n    throw new Error('crontab_field_empty');\n  }\n\n  if (trimmed === '*') {\n    const values = new Set<number>();\n    insertCrontabRange(values, min, max, 1, normalizeDayOfWeek);\n    return { values, wildcard: true };\n  }\n\n  const values = new Set<number>();\n  const segments = trimmed.split(',');\n  if (segments.length === 0) {\n    throw new Error('crontab_field_empty');\n  }\n  segments.forEach((segment) => {\n    const normalizedSegment = segment.trim();\n    if (!normalizedSegment) {\n      throw new Error('crontab_segment_empty');\n    }","sourceCodeStart":502,"sourceCodeEnd":538,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/pages/WakeupTasksPage.tsx#L502-L538","documentation":"parseCrontabField trims the whole field string first; if the result is empty there is nothing to parse for that cron position, so it throws 'crontab_field_empty'. The library requires every one of the five fields to be non-empty.","triggerScenarios":"parseCrontabField called with '' or a whitespace-only string for any of the five fields, e.g. parseCrontabExpression('  *  *  *  ') after split producing an empty part, or direct calls with an empty field.","commonSituations":"A saved crontab string with missing fields due to a truncated export/import; UI input allowing submission before all five fields are filled; splitting on whitespace that collapses leading/trailing spaces into fewer tokens (then parts.length check may not fire but empty parts can appear from other split paths).","solutions":["Supply a value for the empty field, using '*' if any value is acceptable (e.g. '* * * * *').","Trim/repair the stored crontab string before parsing.","Validate the expression with parseCrontabExpression in a try/catch at form level and require all five fields before saving."],"exampleFix":"// before\nparseCrontabExpression('30   * *');       // missing fields / empty parts\n// after\nparseCrontabExpression('30 * * * *');     // all five fields present","handlingStrategy":"validation","validationCode":"const fieldOk = (f: string) => f.trim().length > 0;\nconst parts = expr.trim().split(/\\s+/);\nconst allFieldsOk = parts.length === 5 && parts.every(fieldOk);","typeGuard":"const isNonEmptyField = (f: unknown): f is string =>\n  typeof f === 'string' && f.trim().length > 0;","tryCatchPattern":"try {\n  const parsed = parseCrontabExpression(expr);\n} catch (e) {\n  if ((e as Error).message === 'crontab_field_empty') {\n    setFieldError('every crontab field is required; use * for any value');\n  }\n}","preventionTips":["Require all five fields in the UI before enabling submit","Default empty fields to '*' rather than submitting an empty string","Trim stored crontab strings and repair truncated values on load"],"tags":["validation","crontab","input-parsing"],"backgroundTag":"crontab-field-empty","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}