Last Weekday Cron Expression Mistakes: 2026 Fixes
Why does your "last weekday of the month" cron job keep firing on weekends?
You wanted the job to run on the final Friday — or Thursday, or whatever the last working day happens to be — of every month. Instead, it fired on Saturday at 11:59 PM. Or it didn't fire at all. Or it fired twice. If you're staring at a six-character cron expression wondering which asterisk betrayed you, you're in exactly the right place. Let's walk through the most common blunders people make when scheduling end-of-month weekday jobs — and fix each one using a visual crontab builder so you don't have to decode cryptic syntax by candlelight.
How do you write a cron expression for the last weekday of every month?
Here's the short answer: standard five-field Unix cron cannot express "the last weekday of every month" in a single expression. You read that correctly. The classic * * * * * format has no concept of "last" anything — no L modifier, no "nearest weekday" token, nothing.
What you can do depends on your runtime environment:
- Quartz Scheduler supports the
Lmodifier. The expression0 0 12 ? * 1-5Lmeans "noon on the last weekday of the month" — but the syntax is finicky and the day-of-week field must use?in the day-of-month slot. - Spring's @Scheduled with SpEL can approximate it, but you'll often need a custom condition bean.
- Cloud schedulers (AWS EventBridge, Google Cloud Scheduler) each have their own quirks and some support
LWwhile others don't.
The #1 mistake here: pasting a Quartz-style L expression into a system that expects standard cron and assuming it'll just work. It won't. It'll either silently fail or throw a parse error at deployment — usually at 5 PM on a Friday.
The fix
Before writing a single character, open a visual crontab builder that lets you select your target runtime (Quartz, Spring, standard cron, AWS EventBridge). The builder should adapt its available modifiers to match. If it doesn't know what runtime you're targeting, it can't validate L or LW — and neither can you, reliably.
What does the LW modifier actually do in cron?
The LW modifier — supported by Quartz and a handful of other schedulers — means "the last weekday (Monday through Friday) of the month." It's a combination of L (last) and W (nearest weekday), but it operates on the day-of-month field, not day-of-week.
A valid Quartz expression for "run at 9:00 AM on the last weekday of every month" looks like this:
0 0 9 ? * LW
Let's break that down field by field:
0— seconds0— minutes9— hour (9 AM)?— day of month (must be?when day-of-week is specified; Quartz requires one of the two to be?)*— every monthLW— last weekday of the month
The #2 mistake: people try to use LW in the day-of-week field. It belongs in day-of-month. If you put it in the wrong field, the parser will either reject it or — worse — interpret it as something nonsensical and schedule your job for a random Tuesday in 1997.
The fix
In your visual crontab builder, look for a field labeled "Day of Month" and find the option for "Last Weekday" or "Last Working Day." A good builder will insert LW in the correct field automatically and set the opposing day-of-week field to ? for Quartz or * for other runtimes. If you're hand-editing after using the builder, double-check which field you're modifying.
Why is my last-weekday cron job not firing at all?
This is the most common support ticket in the cron-tooling world, and nine times out of ten the culprit is a runtime mismatch. You wrote a Quartz expression. Your scheduler is AWS EventBridge. EventBridge doesn't support LW. Silent failure.
Here's a quick compatibility reference:
| Runtime | Supports LW? | Supports L? | Alternative |
|---|---|---|---|
| Quartz | Yes | Yes | — |
| AWS EventBridge | No | No | Use a Lambda with date logic |
| Google Cloud Scheduler | No | No | Use a Cloud Function wrapper |
| Standard Unix cron | No | No | Use a wrapper script |
| Spring Boot | Partial | No | Use SpEL + condition |
The #3 mistake: assuming cross-platform compatibility. Cron is not cron is not cron. The five-field format is a de facto standard, but the extended modifiers (L, W, LW, #) are implementation-specific add-ons.
The fix
If your scheduler doesn't support LW, you have two realistic options:
Option A — Wrapper script: Schedule a daily job at 9 AM (0 0 9 * * *) and inside the script, check whether today is the last weekday of the month. If not, exit. If yes, run the actual task. This is ugly but bulletproof and works everywhere.
Option B — Three-expression approach: If you know the last day of the month always falls between the 28th and the 31st, and you only care about weekdays, you can schedule jobs on the 28th, 29th, 30th, and 31st — then have your script exit if it's already run this month or if today is a weekend. It's not elegant, but it's what production teams actually ship.
How do I validate a cron expression before deploying it?
You wouldn't push untested code to production. Yet every day, thousands of engineers paste raw cron expressions into production schedulers and hope for the best. The #4 mistake: skipping validation because "it's just a cron expression."
A proper visual crontab builder should offer three validation layers:
1. Syntax validation
The builder should parse your expression against the specific runtime you've selected and flag any unsupported tokens. If you type LW while AWS EventBridge is selected, it should tell you immediately — not after deployment.
2. Next-fire preview
This is the feature that saves careers. The builder should show you the next 5–10 execution dates. Here's a concrete example: for 0 0 9 ? * LW in Quartz, the next fire times in early 2025 would be:
- January 31, 2025 (Friday — last weekday of January)
- February 28, 2025 (Friday — last weekday of February)
- March 31, 2025 (Monday — last weekday of March)
- April 30, 2025 (Wednesday — last weekday of April)
- May 30, 2025 (Friday — May 31 is a Saturday, so the last weekday is the 30th)
Notice May: the 31st is a Saturday, so LW correctly rolls back to Friday the 30th. If your preview shows May 31, your expression is wrong. If it shows May 31 and it's a Saturday in the preview, your runtime doesn't support LW and is treating it as "last day of month."
3. Edge-case testing
February is the cruelest month for cron. In a leap year, February has 29 days. In 2024, February 29 was a Thursday — so LW fires on the 29th. In 2025, February 28 is a Friday — so LW fires on the 28th. But what about February 2026? The 28th is a Saturday, so LW should fire on Friday the 27th. If your builder's preview doesn't account for this, find a better builder.
What's the most common mistake when using a visual crontab builder for last-weekday scheduling?
The #5 mistake — and this one is sneaky — is trusting a builder that doesn't ask which runtime you're targeting. Generic cron expression generators are the worst offenders. They'll happily let you construct 0 0 9 * * LW in a five-field format, show you a pretty calendar, and never mention that no standard cron daemon on Earth will parse LW in a five-field expression.
Here's the thing: LW requires a six-field expression (with a seconds field) in Quartz. The five-field format doesn't even have a slot for it in most implementations. If your builder outputs five fields with LW, it's lying to you.
The fix
Use a builder that:
- Asks for your runtime first — before you touch a single dropdown.
- Outputs the correct number of fields — five for Unix cron, six for Quartz, JSON for AWS EventBridge.
- Shows a next-fire preview with real dates — not just "last Friday of month" in plain English, but actual calendar dates with day names.
- Flags unsupported combinations — if you select "last weekday" and your runtime is standard cron, it should refuse to generate the expression and suggest the wrapper-script approach instead.
Should I just use a wrapper script instead of fighting with cron syntax?
Honestly? For a lot of teams, yes. If you're running on AWS EventBridge, Google Cloud Scheduler, or a plain old Linux box with standard cron, the LW modifier simply isn't available. You can bang your head against the documentation or you can ship a 10-line wrapper script that checks the date and exits early.
Here's a pragmatic approach that production teams actually use:
Schedule a daily job: 0 0 9 * * * — runs every day at 9 AM. Inside the script:
- Check if today is a weekend. If yes, exit.
- Check if today is the last day of the month. If yes, run the task.
- Check if tomorrow is a weekend and tomorrow is the first of the next month. If yes, run the task today (this catches the Friday-before-a-weekend-month-end scenario).
- Otherwise, exit.
This catches every edge case: leap years, months ending on Saturday, months ending on Sunday, and the dreaded New Year transitions. It's not as sexy as a single LW token, but it works everywhere, and you can test it with unit tests instead of waiting for the calendar to cooperate.
How do I choose the right visual crontab builder for this use case?
Not all builders are created equal. For last-weekday scheduling specifically, you need one that handles extended modifiers. Here's what to look for:
Must-have features
- Runtime selector — Quartz, Spring, AWS, GCP, standard cron. Non-negotiable.
- LW / L / W support in the day-of-month picker, not just day-of-week.
- Next-fire preview showing at least 10 upcoming dates with full timestamps.
- Time zone awareness — "last weekday" means different things in UTC vs. local time. A job that fires at 9 AM UTC on the last weekday of the month might fire on a different calendar day than 9 AM Pacific.
Nice-to-have features
- Human-readable output — "At 09:00 AM, on the last weekday of the month" so you can sanity-check the intent.
- Export to multiple formats — generate the Quartz expression, the AWS EventBridge JSON, and the wrapper-script pseudocode from the same input.
- Shareable URLs — so you can send your expression to a teammate for review before deploying.
The #6 mistake: using a builder that generates a pretty expression but doesn't validate it against your actual runtime. A builder without runtime awareness is just a fancy text editor with extra steps.
Final sanity check before you ship
Before you deploy any last-weekday cron expression, run through this checklist:
Did you verify the field count matches your runtime? Quartz needs six fields, standard cron needs five, and AWS EventBridge needs a JSON schedule expression — not a cron string with LW tacked on the end.
Did you check the next-fire preview for at least the next 12 months? Pay special attention to February, months ending on a Saturday or Sunday, and December-to-January transitions. If your preview looks correct through February 2026 (where the last weekday is Friday the 27th), you're in good shape.
Did you confirm the time zone? A job scheduled at 0 0 9 ? * LW in UTC fires at 4 AM Eastern during DST and 5 AM Eastern otherwise. If your task depends on business hours in a specific region, hard-code the time zone in your scheduler configuration — don't leave it to chance.
Did you set up alerting for missed runs? If your last-weekday job fails silently on the final Friday of the month and nobody notices until the following month, the cron expression wasn't your problem — observability was. Pair every critical scheduled job with a dead-letter queue or a "did it run?" check.
The Bottom Line for 2026
Last-weekday scheduling is one of those problems that looks trivial until you're debugging a production incident at 5 PM on a Friday because the monthly report didn't generate. The cron expression syntax is the easy part. The hard part is understanding which runtime you're targeting, which modifiers it supports, and where the edge cases hide.
Here's the summary of every mistake covered in this article:
- Mistake #1: Assuming LW works everywhere. It doesn't. It's Quartz-specific and unsupported by standard cron, AWS EventBridge, and Google Cloud Scheduler.
- Mistake #2: Confusing LW (last weekday of month) with 5L or FRIL (last Friday of month). They're different tokens with different behaviors, and mixing them up means your job fires on the wrong day.
- Mistake #3: Ignoring time zone shifts. The last weekday in UTC might not be the last weekday in your local time zone, especially near month boundaries.
- Mistake #4: Skipping validation. A cron expression is code. Test it like code — syntax validation, next-fire preview, edge-case testing.
- Mistake #5: Trusting a generic builder that doesn't ask which runtime you're targeting. If it outputs five fields with LW, it's wrong.
- Mistake #6: Using a builder that generates a pretty expression but never validates it against your actual runtime. That's not a tool — that's a liability.
The fixes heading into 2026 are straightforward. Use a runtime-aware crontab builder that shows you real next-fire dates. Validate against February 2026, where the last weekday falls on Friday the 27th — if your tool gets that right, it understands LW. If it doesn't, switch tools. And if your runtime doesn't support LW at all, don't force it. Ship the wrapper script. Your future self, debugging at 5 PM on the last Friday of the month, will thank you.
The best cron expression is the one you never have to think about again after deploying it. Get the syntax right, validate the dates, and move on to problems that actually need your attention.
Frequently Asked Questions
How do I write a cron expression for the last weekday of every month?
Standard Linux cron does not natively support the "last weekday" directly, but many modern cron implementations use the LW syntax in the day-of-month field. Using a visual crontab builder, you can easily select "Last Weekday" from a dropdown menu to automatically generate the correct expression for your specific environment.
What does the LW cron expression mean?
In advanced cron expressions like Quartz, LW stands for "Last Weekday" and is used in the day-of-month field to target the final Monday through Friday of the month. This is highly useful for scheduling end-of-month reporting tasks on the last business day. A visual crontab generator will automatically apply this syntax when you choose the last weekday option.
How can I validate a cron expression for the last weekday?
You can validate your cron expression by pasting it into a visual crontab builder or an online cron validator. These tools will parse the LW syntax, confirm if it's valid for your specific scheduler, and display the next scheduled execution dates so you can verify it falls on the correct weekday.
Can I generate a last business day cron job without coding?
Yes, a visual crontab builder allows you to generate complex cron schedules without memorizing syntax. Simply navigate to the day-of-month section, select the "Last Weekday" or "Last Business Day" option, and the tool will generate the correct expression for you to copy and paste.
How do I run a cron job on the last Friday of every month?
To target the last Friday specifically, you can use the 5L syntax in the day-of-week field, where 5 represents Friday and L represents last. A visual crontab builder makes this easier by letting you select "Friday" and "Last Occurrence" from a menu, generating and validating the 5L syntax instantly.
Does standard Linux crontab support the last weekday of the month?
The standard Linux crontab (Vixie cron) does not support L or LW modifiers natively. To achieve this in standard Linux, you typically run a job on the last three days of the month and use a shell script to check if the current day is the final weekday. However, many modern visual crontab generators support these advanced syntaxes for use with systems like Quartz or Spring.
Why is my last weekday cron expression not working?
If your cron expression is failing, it is likely because your execution environment uses standard Vixie cron, which doesn't support the LW syntax. You should validate your expression in a visual crontab builder to check for syntax errors, and ensure your task scheduler actually supports advanced cron modifiers.
How do I test my last weekday cron schedule before deploying?
You can test your schedule by inputting the generated cron expression into a visual crontab validator. The tool will calculate and display a list of future execution dates, allowing you to visually confirm that the jobs only trigger on the final weekday of each month.
What is the best visual crontab builder for advanced cron expressions?
The best visual crontab builders feature an interactive UI where you can select specific days, weekdays, and last-day modifiers without typing code. Look for a tool that also includes a built-in validator and a next-run preview to ensure your LW or 5L expressions are perfectly accurate.
How do I handle holidays when scheduling a cron job for the last weekday?
Cron expressions alone cannot account for public holidays, as they strictly follow calendar days and standard weekdays. To handle holidays, you must generate the last weekday cron expression in your visual builder, but then use a script wrapper to check a holiday API before executing the actual task.