Control-M Job Stuck in Wait Condition: Fix Condition Dates & ODAT
Control-M job stuck in wait condition? Learn how condition dates and ODAT cause it—and the exact steps to resolve it fast in production.
20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.
- ✓Basic familiarity with Control-M job scheduling and the Planning domain
- ✓Access to Control-M Monitoring and Tools (or the AAPI/CLI)
- ✓Understanding of Control-M conditions (add/delete) and job dependencies
Check the condition's date in the job's wait-for list versus the condition's actual date in the Conditions manager. If the job waits for COND-0101 but the condition was added as COND-0102, it will never trigger. Fix by adding the correct condition or adjusting the job's ODAT.
Imagine a relay race where the baton is only valid on a specific day. If you're waiting for yesterday's baton, you'll stand there forever. Control-M conditions work the same way—each condition has a date stamp, and your job only accepts the baton if the dates match.
Your job's been sitting in Wait Condition for six hours, and the upstream job finished successfully. You've checked the logs, the dependencies, the agent—everything looks fine. But it's not. The condition date is wrong, and you're about to learn why.
This is the silent killer of Control-M schedules: condition dates and ODAT. They don't throw errors. They don't alert. They just make your job wait forever, and you only find out when the SLA is blown. I've seen a payments batch stuck for 12 hours because someone manually added a condition with today's date instead of the job's ODAT.
By the end of this, you'll know exactly how to diagnose a stuck job in under two minutes, fix condition date mismatches, and design your flows so this never happens again. No fluff—just the production patterns that work.
Why Condition Dates Exist: The Problem They Solve
Before condition dates, Control-M conditions were global and date-agnostic. If a job added FILE_READY, any job waiting for FILE_READY would trigger—even if it was from a different day's run. This caused chaos in daily batches: a job from Monday would trigger Tuesday's downstream jobs, leading to duplicate processing and data corruption.
Condition dates solve this by scoping conditions to a specific date (usually the ODAT). When a job adds a condition, it stamps it with its ODAT. When a job waits for a condition, it only accepts conditions with a matching date. This ensures that Monday's conditions only trigger Monday's jobs.
The trade-off? If dates don't match—due to manual reruns, timezone issues, or misconfiguration—jobs wait forever. There's no error, no warning. Just silence.
ctmcontb -ADD <cond> <date>.ODAT: The Anchor for All Condition Dates
ODAT (Order Date) is the scheduled date of a job order. It's not the actual run date—it's the date the job is supposed to run for. When you schedule a job daily, each day's order gets an ODAT. All conditions added or waited for by that job use its ODAT.
Why does this matter? Because reruns and manual orders can have different ODATs. If you manually order a job for yesterday's date, its ODAT is yesterday, and it will add/wait for conditions with yesterday's date. If the upstream job ran today, its conditions have today's date—mismatch.
This is the #1 cause of stuck jobs after a rerun. The fix is to ensure all jobs in a flow share the same ODAT, or to manually adjust conditions.
How to Diagnose a Stuck Job in Under 2 Minutes
Forget digging through logs. The fastest way is to compare what the job waits for with what conditions exist. Use the Control-M CLI or GUI.
Step 1: Get the job's wait-for conditions and ODAT. Step 2: List all conditions and their dates. Step 3: Spot the mismatch. That's it.
Here's the exact command sequence I use in production.
ctmpsm with no arguments to open the interactive menu and browse active jobs, or check the Monitoring domain in the Control-M web interface for a compact view including ODAT and conditions.The Manual Condition Trap: Why Adding Conditions Often Fails
You see the job waiting, you add the condition manually, and... nothing. The job still waits. Why? Because you added the condition with today's date, not the job's ODAT.
Control-M's ctmcontb -ADD <cond> <date> takes the date explicitly (MMDD), so if you pass today's date instead of the job's ODAT, your condition is useless. If the job's ODAT is yesterday (due to a rerun), always specify the date explicitly.
This is the mistake that cost one team 6 hours of debugging. Don't be them.
Designing Flows That Don't Get Stuck: Best Practices
The best fix is prevention. Design your flows so condition dates always align. Use these patterns:
- Use relative dates in conditions: Instead of hardcoding dates, use Control-M's built-in date variables (e.g.,
%%ODAT) so conditions automatically match the job's ODAT. - Avoid manual condition additions: Automate condition management via the API or scripts that pull the correct ODAT.
- Monitor condition mismatches: Set up alerts for jobs in Wait Condition longer than expected. Use Control-M's SLA management.
- Standardize ODAT across flows: When rerunning, rerun the entire flow with the same ODAT, not individual jobs.
When to Use ODAT Override (And When It Bites You)
ODAT override lets you force a job to use a specific date, regardless of schedule. It's powerful for backfills and reruns, but it's also a common source of stuck jobs.
Use it when: you need to reprocess data for a specific date, or when recovering from a failure that requires a different date context.
Don't use it when: you're just trying to 'make the job run now'—that's what manual ordering is for. ODAT override changes the condition dates, which can break downstream dependencies if not coordinated.
Always communicate ODAT overrides to the team. A silent override can cause hours of confusion.
Real-World Pattern: Handling Late-Arriving Files with Condition Dates
A common scenario: a file arrives late, and you need to trigger a job that's already past its scheduled time. If you just add the condition with today's date, it might not match the job's ODAT (which could be yesterday if the job was scheduled for yesterday but hasn't run yet).
The pattern: use a condition with a relative date that matches the job's ODAT. Or, use a file watcher job that adds the condition with the correct ODAT.
Here's how a media company handles late video files: they have a file watcher job that runs every hour, checks for files, and adds a condition with the current ODAT. The downstream processing job waits for that condition. If the file arrives late, the watcher adds the condition with the correct date, and the job triggers.
The Dark Side: When Condition Dates Cause Silent Failures
Condition dates are great for isolation, but they can also cause silent failures that are hard to debug. For example, if a job adds a condition with a date that no downstream job expects, the condition sits there forever, consuming resources and confusing operators.
Another issue: conditions with dates in the past can accumulate. Control-M doesn't automatically clean them up unless you configure retention. Over time, the Conditions manager becomes cluttered, and finding the right condition becomes a nightmare.
Mitigation: Set up regular cleanup of old conditions. Use Control-M's condition retention policies or a scheduled job that deletes conditions older than X days.
ctmcontb -LIST "" "" to see wait-for conditions first.The 12-Hour Payments Batch That Never Ran
PAYMENTS_READY with date 0101 (its ODAT), but the downstream job had ODAT 0102 (due to a manual rerun). The condition dates didn't match, so the downstream job ignored it.ctmcontb -LIST PAYMENTS_READY "*" to confirm ODAT. Then add condition with the correct date: ctmcontb -ADD PAYMENTS_READY 0102. Job triggered immediately.- Always check ODAT before manually adding conditions—a date mismatch will make you look like an idiot for hours.
ctmwhy <orderno> to see why the job is waiting and which in-condition it expects. 2. Run ctmcontb -LIST <condition> "*" to see existing conditions and dates. 3. Compare dates; if mismatch, add condition with correct date: ctmcontb -ADD <condition> <ODAT>.ctmcontb -LIST "" "" to see added conditions. 3. If date is wrong, manually add correct condition with ctmcontb -ADD <cond> <date> or rerun job with correct ODAT.ctmcontb -ADD <cond> <date> with the job's ODAT. 3. Check Control-M logs for condition add errors.ctmwhy <orderno>ctmcontb -LIST <condition_name> "*"ctmcontb -ADD <cond> <ODAT>| File | Command / Code | Purpose |
|---|---|---|
| diagnose_stuck_job.sh | ctmwhy JOB_NAME | grep -E 'ODAT|Wait For' | How to Diagnose a Stuck Job in Under 2 Minutes |
| manual_condition_fix.sh | ctmcontb -ADD PAYMENTS_READY $(date +%m%d) | The Manual Condition Trap |
| odat_override.sh | ctmorder -FOLDER FOLDER_NAME -NAME JOB_NAME -ODATE 0102 -FORCE y | When to Use ODAT Override (And When It Bites You) |
| file_watcher.sh | FILE_PATH="/data/incoming/video_$(date +%Y%m%d).mp4" | Real-World Pattern |
| cleanup_conditions.sh | CUTOFF_DATE=$(date -d '7 days ago' +%m%d) | The Dark Side |
Key takeaways
ctmcontb -ADD <cond> <date> (date is MMDD).Interview Questions on This Topic
How does Control-M handle condition dates when a job is manually rerun with a different ODAT?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.
That's Control-M. Mark it forged?
3 min read · try the examples if you haven't