Skip to content
Home DevOps AutoSys Job Dependencies and Conditions — Controlling Job Chains

AutoSys Job Dependencies and Conditions — Controlling Job Chains

Where developers are forged. · Structured learning · Free forever.
📍 Part of: AutoSys → Topic 14 of 30
Master AutoSys job conditions: success(), failure(), done(), notrunning(), and complex AND/OR conditions.
⚙️ Intermediate — basic DevOps knowledge assumed
In this tutorial, you'll learn
Master AutoSys job conditions: success(), failure(), done(), notrunning(), and complex AND/OR conditions.
  • AutoSys provides four condition types: success(), failure(), done(), and notrunning()
  • done() is the right choice for cleanup or notification jobs that must run regardless of the upstream job's outcome
  • Conditions can reference jobs in other boxes or boxes themselves
Job Dependency Chain Job Dependency Chain. success() conditions chain jobs together · extract_job · runs at 22:00 · transform_job · success(extract) · load_job THECODEFORGE.IOJob Dependency Chainsuccess() conditions chain jobs together extract_jobruns at 22:00 transform_jobsuccess(extract) load_jobsuccess(transform) report_jobsuccess(load) notify_jobdone(report)THECODEFORGE.IO
thecodeforge.io
Job Dependency Chain
Autosys Job Dependencies Conditions
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
Quick Answer
  • AutoSys conditions define when a job can start based on other jobs' statuses
  • Four functions: success(), failure(), done(), notrunning()
  • Combine with AND / OR and parentheses for complex rules
  • Conditions evaluate after the referenced job finishes (or stops running for notrunning)
  • Common failure: using success() for cleanup jobs — use done() instead
  • In production, complex conditions with many AND/OR masks timing issues — test with autorep first
🚨 START HERE

Quick Debug Cheat Sheet for AutoSys Conditions

Use these commands and checks to resolve condition-related issues in production.
🟡

Job won't start (pending with condition_unmet)

Immediate ActionRun autorep -q <jobname> to see exact condition string and status of referenced jobs.
Commands
autorep -q <jobname> | grep -i condition
autorep -q <referenced_job> | grep -i status
Fix NowIf condition references a job that is INACTIVE/ON HOLD, either remove the condition, change to done(), or force the referenced job to run with sendevent -E FORCE_START -J <refjob>.
🟡

Job runs but shouldn't have (early trigger)

Immediate ActionExamine the condition for unintended OR splits. This is especially common when multiple upstream jobs complete at different times.
Commands
autorep -q <jobname> | grep condition
Check job log: autorep -q <jobname> -l0 | tail -20
Fix NowAdd parentheses to group OR conditions if possible, or separate into a box job to control trigger granularity.
🟡

Cleanup job never fires after failure

Immediate ActionCheck condition: if it uses success(), change to done().
Commands
autorep -q <cleanup_job> | grep condition
autorep -q <upstream_job> | grep status
Fix NowUpdate the cleanup job's condition to: condition: done(<upstream_job>) and re-schedule.
Production Incident

The Silent Deadlock: A job never ran because its predecessor was INACTIVE

A nightly ETL pipeline stalled because job B had condition: success(job_A) but job_A had never run (INACTIVE). The condition never evaluated to true, and the pipeline waited forever.
SymptomThe ETL pipeline started at 2 AM but job B never ran. No error, no alert. The entire batch was stuck.
AssumptionThe engineer assumed that if job_A wasn't running, the condition would eventually resolve once job_A ran. But job_A was not scheduled that day — it was placed on hold as part of a calendar exception.
Root causeThe condition success(job_A) evaluates to false as long as job_A has not completed with exit code 0. Since job_A was INACTIVE (never ran), the condition remained false indefinitely. AutoSys does not treat 'never ran' as a failure — it simply blocks the dependent job.
FixChanged the condition to done(job_A) to make job_B run regardless of job_A's outcome. Alternatively, scheduled job_A with a forced run or used notrunning() to allow job_B to run if job_A was never started.
Key Lesson
success() and failure() require the referenced job to have run at least once and terminated. INACTIVE jobs never produce a true evaluation.For jobs that must always run after a predecessor, even if the predecessor is skipped, use done() or notrunning() with an additional calendar check.Always verify referenced jobs are scheduled and not on hold. Use autorep -q job_A to check status before the batch window.
Production Debug Guide

Symptom → Action: Diagnose dependency failures fast

Job stuck in PENDING or ACTIVATED but never runsCheck condition of the stuck job: autorep -q <jobname> | grep 'condition:'. Verify referenced jobs with autorep -q <refjob> | grep 'status:'. If referenced job is INACTIVE or FAILED, that explains the block.
Job runs earlier than expectedReview condition logic for unintended OR conditions. A condition with success(A) OR success(B) will trigger when either completes — even if the other hasn't run. Use parentheses carefully.
Cleanup job never runs after a failureCheck if condition uses success() instead of done(). Cleanup jobs should always use done() so they fire regardless of upstream exit code.
Job starts but immediately fails because a dependency job is still runningThe condition notrunning() evaluates to true as soon as the referenced job is not running, even if it hasn't started yet. Use success() or done() for sequential execution, not notrunning().

Dependency management is AutoSys's superpower — the thing that justifies its cost over cron. The condition attribute lets you express exactly what must be true before a job can start. You can chain hundreds of jobs with precise dependency rules, and AutoSys handles the orchestration automatically.

This article covers all condition types, how to combine them, and the gotchas that cause dependency chains to break in production.

The four condition functions

  • success(job): Triggers when the referenced job completes with exit code 0
  • failure(job): Triggers when the referenced job completes with non-zero exit code
  • done(job): Triggers when the referenced job completes, regardless of exit code
  • notrunning(job): Triggers when the referenced job is not currently in RUNNING state

Each function takes the name of another job (or box) as argument. The condition is evaluated when the referenced job changes state. For success(), failure(), and done() the state change is completion/termination. For notrunning() it's any transition out of RUNNING, including job start (from not-running to running also triggers, but that's not typical usage).

condition_types.jil · BASH
1234567891011121314151617181920
/* success(job) — run after job_a completes successfully */
condition: success(job_a)

/* failure(job) — run after job_a fails (error handling job) */
condition: failure(job_a)

/* done(job) — run after job_a is done, regardless of success or failure */
condition: done(job_a)

/* notrunning(job) — run when job_a is NOT currently running */
condition: notrunning(job_a)

/* Combining conditions with AND */
condition: success(job_a) AND success(job_b)

/* Combining with OR */
condition: success(job_a) OR success(job_b)

/* Complex condition */
condition: success(job_a) AND (success(job_b) OR success(job_c))
📊 Production Insight
A common production mistake is using success() for cleanup jobs.
If the main job fails, the cleanup never runs — leaving temp files or stale locks.
Always use done() for cleanup, notification, or logging jobs.
🎯 Key Takeaway
Pick the exact condition matching your intent.
success != done. failure != notrunning.
Get it wrong and your pipeline either deadlocks or runs ahead unexpectedly.
Choosing the right condition function
IfJob must run only if upstream succeeded
UseUse success(upstream_job)
IfJob must run only if upstream failed
UseUse failure(upstream_job)
IfJob must run regardless of upstream outcome
UseUse done(upstream_job)
IfJob must run when upstream is not running (but may have never run)
UseUse notrunning(upstream_job) — but be careful: it evaluates true if the job hasn't started yet

Cross-box dependencies

Conditions can reference jobs in other boxes or standalone jobs. This lets you build workflows that span multiple boxes.

When a condition references a job outside the current box, AutoSys uses global job name resolution. The referenced job must have a unique name across the entire AutoSys instance — if duplicates exist, the condition may resolve to the wrong job.

You cannot condition on a box name to wait for all children of that box. Instead, condition on the last child job of that box, or use done(box_name) which completes only when all children of that box have finished.

cross_box.jil · BASH
12345678
/* Job in reporting_box waits for a job in etl_box */
insert_job: generate_daily_report
job_type: CMD
box_name: reporting_box
command: /scripts/daily_report.sh
machine: report-server
owner: batch
condition: success(etl_load_job)   /* etl_load_job is in etl_box */
🔥Cross-box conditions must reference the job name, not the box name
You condition on individual jobs, not on boxes. To wait for an entire box to complete, condition on the last job in that box — or use done() on the box job itself, which completes when all its children are done.
📊 Production Insight
When crossing boxes, ensure referenced job names are globally unique.
AutoSys does not warn on duplicate names — it picks one arbitrarily.
Enforce a naming convention like <box>_<job> to avoid collisions.
🎯 Key Takeaway
Cross-box conditions are powerful but fragile.
Unique job names are not optional — they're a deployment prerequisite.
Always test cross-box dependencies in a non-production environment first.

The done() condition and why it matters

done() is often overlooked but very useful. It lets a job run after another job completes regardless of whether that job succeeded or failed. This is perfect for cleanup or notification jobs that should always run.

Use cases
  • Cleanup temp files after a job completes
  • Send status email with success/failure info
  • Update a monitoring system with job execution results

Because done() triggers after any termination — including kill or timeout — it ensures your post-processing runs reliably.

done_condition.jil · BASH
123456789101112131415
/* Cleanup job: runs whether or not main job succeeded */
insert_job: cleanup_temp_files
job_type: CMD
command: /scripts/cleanup.sh
machine: server01
owner: batch
condition: done(main_etl_job)   /* runs regardless of main_etl_job outcome */

/* Notification job: always sends status email */
insert_job: send_batch_status_email
job_type: CMD
command: "/scripts/notify.sh --job main_etl_job"
machine: server01
owner: batch
condition: done(main_etl_job)
📊 Production Insight
done() triggers on job kill (sendevent -E KILL) as well as normal completion.
If you need to differentiate between success and failure, use success() and failure() on separate branches.
done() is also useful for dependencies that must fire after a job times out.
🎯 Key Takeaway
done() is the safest default for side-effect jobs.
It guarantees execution after the upstream finishes — no matter how.
Just remember: it won't fire if the upstream never runs.
When to use done() vs success()
IfUpstream job's result matters
UseUse success() or failure()
IfMust run regardless of outcome
UseUse done() — cleanup, notification, monitoring
IfUpstream job may be skipped (INACTIVE)
Usedone() does not trigger if job never runs. Use notrunning() with caution, or force-run the upstream job.

notrunning(): preventing concurrent execution

The notrunning() condition triggers when the referenced job is not in RUNNING state. This is useful for throttling or serialising jobs that share a resource.

Important caveat: notrunning() evaluates true if the referenced job has never run (INACTIVE status). That means if you write condition: notrunning(app_job) and app_job hasn't been scheduled yet, the condition is immediately true — the dependent job will start right away, likely not what you intended.

To prevent concurrent runs of the same job, use condition: notrunning(app_job) where app_job is the same job. AutoSys supports self-referencing conditions for this purpose. When used on the same job, the condition is met only after the previous instance finishes.

notrunning_condition.jil · BASH
123456789101112131415
/* Report generator: only one instance at a time */
insert_job: daily_report
job_type: CMD
command: /scripts/report.sh
machine: server01
owner: batch
condition: notrunning(daily_report)   /* self-reference prevents overlap */

/* Resource-dependent job: wait until resource job finishes */
insert_job: process_batch
job_type: CMD
command: /scripts/process.sh
machine: server02
owner: batch
condition: notrunning(resource_cleanup)   /* ensures cleanup is not running */
⚠ notrunning() evaluates true when the referenced job has never run
If the referenced job is INACTIVE (not scheduled or on hold), notrunning() returns true. This can cause dependent jobs to start unexpectedly. Always verify the referenced job's schedule and status.
📊 Production Insight
Self-referencing notrunning() is the standard pattern for job serialization.
But if the job is new and never ran, the condition is immediately true — and the job may start before you expect.
Best practice: combine notrunning() with a time condition (start_times) or ensure the referenced job has run at least once.
🎯 Key Takeaway
notrunning() is for concurrency control, not sequential execution.
It checks the current state, not completion history.
For true sequential chaining, use success() or done().

Complex conditions with AND, OR, and parentheses

You can build complex dependency logic by combining conditions with AND and OR operators, and using parentheses to control evaluation order. AutoSys evaluates conditions from left to right, respecting parentheses. There's no NOT operator.

Example: You need a job to run if either Job A and Job B both succeeded, OR Job C succeeded. The condition would be:

condition: (success(job_a) AND success(job_b)) OR success(job_c)

Without parentheses, AND has higher precedence than OR, so: condition: success(job_a) AND success(job_b) OR success(job_c) is equivalent to: condition: (success(job_a) AND success(job_b)) OR success(job_c)

However, it's safer to always use explicit parentheses for readability and to avoid future misinterpretation.

Complex conditions are powerful but can lead to hard-to-debug behaviours. If you have many conditions, consider breaking the logic into intermediate box jobs to simplify each condition.

complex_condition.jil · BASH
1234567891011121314151617181920212223242526272829
/* Real-world example: Run final aggregation if (ETL success AND validation success) OR manual override flag */
insert_job: final_aggregation
job_type: CMD
box_name: reporting_box
command: /scripts/aggregate.sh
machine: batch-server
owner: batch
condition: (success(run_etl) AND success(validate_data)) OR success(manual_override)

/* Use intermediate box to simplify */
insert_job: etl_and_validation_box
job_type: BOX

insert_job: run_etl
job_type: CMD
box_name: etl_and_validation_box
command: /scripts/etl.sh

insert_job: validate_data
job_type: CMD
box_name: etl_and_validation_box
command: /scripts/validate.sh
condition: success(run_etl)

/* Now final job condition becomes simpler */
insert_job: final_aggregation
job_type: CMD
command: /scripts/aggregate.sh
condition: success(etl_and_validation_box) OR success(manual_override)
💡Use boxes to manage complex condition trees
Instead of writing a monster condition with 5 AND/OR clauses, group related jobs into a box. Then condition on the box. This makes your JIL easier to read and debug.
📊 Production Insight
Complex conditions often mask latent issues: one of the referenced jobs might be removed or renamed, but the condition still parses and evaluates to false silently.
When modifying job names, always audit all conditions referencing that job.
Autorep -q <job> will show the condition; use grep to find all jobs that reference a given job name.
🎯 Key Takeaway
Keep conditions simple — use boxes to decompose complex logic.
Always parenthesize to make precedence explicit.
A condition that's hard to read is a condition that will break silently.
🗂 Condition Function Comparison
Choose the right condition based on your dependency need
ConditionTriggers when...Common use case
success(job)Job completed with exit code 0Normal sequential dependency
failure(job)Job completed with non-zero exit codeError handling / failover job
done(job)Job completed (either success or failure)Cleanup / notification jobs
notrunning(job)Job is not currently RUNNINGPreventing concurrent execution

🎯 Key Takeaways

  • AutoSys provides four condition types: success(), failure(), done(), and notrunning()
  • done() is the right choice for cleanup or notification jobs that must run regardless of the upstream job's outcome
  • Conditions can reference jobs in other boxes or boxes themselves
  • AND/OR logic lets you build complex dependency rules — test them carefully before deploying to production
  • Circular dependencies are not detected at definition time; manually review chains for loops
  • notrunning() is for concurrency control, not sequential dependency — it evaluates true if the referenced job never ran

⚠ Common Mistakes to Avoid

    Using success() instead of done() for cleanup jobs
    Symptom

    Cleanup job never runs when the main job fails. Temp files accumulate, disk fills up, future jobs fail.

    Fix

    Change condition to: condition: done(main_job). Cleanup now runs regardless of success or failure.

    Not accounting for jobs in INACTIVE state
    Symptom

    A dependent job with condition: success(job_A) never runs because job_A has never executed (INACTIVE). Pipeline deadlocks silently.

    Fix

    Ensure job_A is scheduled or forced to run. Alternatively, use done() if the dependency is not strict. Use autorep -q job_A to verify status before batch.

    Creating circular dependencies
    Symptom

    Job A depends on B, Job B depends on A. Both stay PENDING forever. No error at definition time.

    Fix

    Manually review dependency chains. Autosys does not detect cycles. Use a tool or script to trace dependency graphs. Break the cycle by redesigning workflow.

    Misunderstanding OR conditions leading to early triggering
    Symptom

    Job starts when one of two upstream jobs succeeds, even though both were expected before starting.

    Fix

    Use AND instead of OR if all dependencies are required. If OR is intended but the trigger happens too early, consider using a box to group the upstream jobs and condition on the box.

    Assuming notrunning() waits for a job to run
    Symptom

    Job with condition: notrunning(resource_job) starts immediately because resource_job never ran and is INACTIVE.

    Fix

    Use success() or done() for sequential dependencies. Only use notrunning() when you specifically want to check runtime state.

Interview Questions on This Topic

  • QWhat is the difference between success() and done() conditions in AutoSys?JuniorReveal
    success() triggers when the referenced job completes with exit code 0 (success). done() triggers when the reference job completes regardless of exit code. So done() runs even if the job failed, while success() only runs on success. Use success() for normal chaining and done() for cleanup or notification jobs that must always execute.
  • QWhat does the condition failure() do?JuniorReveal
    failure(job) triggers when the referenced job completes with a non-zero exit code (failure). It is used for error-handling jobs, such as sending an alert or triggering a recovery process. It does not trigger if the job never ran or was killed.
  • QCan an AutoSys job condition reference jobs in a different box?Mid-levelReveal
    Yes. Conditions can reference any job in the AutoSys instance by its unique name, regardless of box membership. To wait for an entire box, condition on the box job itself, but note that done(box_name) waits for all children of the box to finish. Alternatively, condition on the last job inside the box.
  • QWhat happens if you have a circular dependency in AutoSys conditions?SeniorReveal
    AutoSys does not detect circular dependencies at job definition or scheduling time. The jobs will remain in PENDING state indefinitely, waiting for each other. This causes a silent deadlock. You must manually identify and fix the cycle. Use autorep -q <job> to see conditions and trace dependencies.
  • QWhat condition would you use for a cleanup job that must run whether or not the main job succeeded?JuniorReveal
    Use done(main_job). This ensures the cleanup job runs after the main job completes, regardless of its exit code. If the cleanup should only run on success or failure, use success() or failure() respectively.
  • QHow does the notrunning() condition behave if the referenced job has never run?SeniorReveal
    notrunning() evaluates true when the referenced job is not in RUNNING state. If the job has never run (INACTIVE), it is not running, so notrunning() returns true immediately. This can cause unexpected early triggering. To avoid this, combine notrunning() with a start_times or ensure the referenced job is scheduled.

Frequently Asked Questions

What conditions can I use in AutoSys?

AutoSys provides four condition functions: success(job) — job completed successfully; failure(job) — job failed; done(job) — job completed regardless of outcome; notrunning(job) — job is not currently running. You can combine these with AND and OR operators.

What is the done() condition in AutoSys?

done() triggers when a job has completed, whether it succeeded or failed. It's commonly used for cleanup jobs, status notification jobs, or any job that should always run after an upstream job finishes, regardless of outcome.

Can AutoSys job conditions span multiple boxes?

Yes. The condition attribute can reference any job in the AutoSys instance, regardless of which box it belongs to. The referenced job name must be unique across the instance.

What happens if I create a circular dependency in AutoSys?

AutoSys won't detect circular dependencies at job definition time. The jobs will deadlock — Job A waits for B, Job B waits for A, and neither ever starts. You need to carefully review dependency chains to prevent this.

How do I make a job run after any one of several jobs succeeds?

Use OR in your condition: condition: success(job_a) OR success(job_b). The job starts as soon as either condition becomes true.

What is the difference between notrunning() and success()?

notrunning() checks if a job is currently not running (state is not RUNNING). It evaluates true if the job never ran, finished, or was killed. success() only evaluates true after the job has completed with exit code 0. Use success() for sequential chaining, notrunning() for serialization.

🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousAutoSys Job Scheduling and Time ConditionsNext →AutoSys Calendars and run_calendar
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged