Conceptual editorial illustration
AI-generated editorial illustration. The measurements and experiment results are presented separately below.

A command can succeed in your terminal because that shell already has the right executable search path, working directory and configuration. A scheduled invocation need not have the same context. Making the executable path absolute solves only one of those dependencies.

A 2012 Server Fault canonical troubleshooting question provides the broader problem statement. We narrowed it to three execution-context differences, checked the Linux manuals and ran six controlled cases. We did not run cron or reproduce every reason a scheduled job can fail.

Observed: a missing command search path returned 127; an absolute executable with the wrong directory returned our worker's input_missing; absolute paths without a required configuration label returned label_missing. Two explicit-context variants succeeded. These are synthetic child-shell tests, not a scheduler reliability test.

First separate dispatch from execution

Before changing the job, establish whether the scheduler invoked it. Check the intended account, installed entry and the scheduler's available logs or captured job output. A missing run and an invoked command that fails are different problems. Our experiment starts at invocation: it cannot diagnose an invalid schedule, a stopped daemon or the wrong account's crontab.

  1. Locate the executable. Check the interpreter and command the job actually uses. The Linux environment manual describes PATH as a list searched for an executable named without a slash. A custom tools directory in one shell's PATH is not evidence that another invocation has it.
  2. Locate the input. An absolute executable can still open ./payload.txt relative to the process's current directory. Make the required directory deliberate or use the application's supported absolute input paths.
  3. Identify required configuration. Declare the job's needed non-secret settings and use its supported mechanism for secrets. A terminal's exported variable is not proof that the scheduler's child receives it.

The cited crontab manual documents environment settings and a default SHELL of /bin/sh for that implementation. Check your installed cron's manual rather than assuming every scheduler uses an identical PATH, directory or startup sequence. Our minimal PATH below is an experiment setting, not a measured cron default.

Six cases, with one dependency exposed at a time

The November 7, 2026 run used Linux 6.8.0-147-generic, Python 3.12.3 and /bin/sh, resolving to dash. A new fixture contains an executable named lab-job, an application directory with a one-line payload, and a neutral directory. The application and tools directory names include spaces.

Every child receives an explicit environment containing only PATH and, when specified, LLAB_JOB_LABEL=demo. This is a non-secret synthetic label. The worker reads the payload, checks the label and emits one fixed line; it does not send messages, access an application database or perform a backup.

Controlled child-shell invocations · no cron daemon
CaseWhat changedExitObserved output
Manual-style baselineTools on PATH, application directory, label supplied0Expected payload and label
Tools missing from PATHKeep the directory and label; PATH is /usr/bin:/bin127lab-job: not found
Absolute executable, relative inputNeutral directory; label supplied20input_missing
Absolute executable and inputNeutral directory; required label omitted21label_missing
Absolute paths plus labelNeutral directory; all required inputs explicit0Expected payload and label
Explicit cd, PATH and labelStart neutral, then set context for the command0Expected payload and label

The exit sequence was 0, 127, 20, 21, 0, 0. Each case checks the exit, stdout and stderr: 18 expectations passed. Exit codes 20 and 21 are deliberately assigned by this worker, not universal cron errors. Even 127 needs context: inspect the accompanying diagnostic rather than assuming every occurrence proves the same root cause.

The baseline is a constructed manual-style context, not a recording of an interactive login shell. Failures did not depend on a real cron daemon: we deliberately changed the command's inputs and observed the resulting failure stage.

Make the context part of the job

One successful fixture uses absolute paths for both the executable and its input, plus its non-secret label. The following illustrates that pattern; the /srv/example... files are placeholders, not installed files or a command to paste into a live job:

LLAB_JOB_LABEL=demo \
'/srv/example tools/lab-job' \
'/srv/example job/payload.txt'

The other successful fixture explicitly changes directory and supplies a scoped PATH and label. This keeps a relative input deliberate:

cd '/srv/example job' &&
PATH='/srv/example tools:/usr/bin:/bin' \
LLAB_JOB_LABEL=demo \
lab-job

The quoted paths preserve spaces. The && prevents the command from running after a failed directory change. These examples show the command portion of a proposed job, not a complete crontab entry. Adapt the actual interpreter, paths, settings, account and output handling to your application, then rehearse without real side effects before enabling its schedule.

Record only the small set of diagnostic values you need. Avoid dumping an entire environment into logs or public troubleshooting posts; it can contain credentials. Importing a whole personal shell profile is also a broader dependency than declaring the particular settings this job needs.

Run the isolated drill

Download and inspect the complete Python script. Use Linux with Python 3.9 or later, its standard library and /bin/sh:

python3 job_context_demo.py --out ./context-drill

Each run creates a new job-context-... child directory, worker and synthetic input. It refuses the drill if lab-job already exists on the minimal system PATH, so the missing-command control cannot run somebody else's program. Each shell call has a five-second timeout. The script has no input option for an existing job, makes no network calls, does not install a schedule and does not delete files.

Read the generated result.json. Each case records its constructed environment, working directory, command, exit and both output streams. Fixture-root paths are normalized to <fixture>; the recorded environments are allowlists, not dumps of the host's environment. Compare the dated Lab result with the expected outcomes. Durations describe these tiny commands on one host, not a performance benchmark or timing guarantee.

What still needs a separate check

A corrected execution context does not establish correct scheduling, permissions, application behavior or recovery. We did not test cron dispatch, PAM or login setup, timezone rules, permissions failures, containers, external credentials or a real service's job. Do not change the job to run as root merely because this synthetic example succeeded.

Once a harmless rehearsal runs in the intended context, use the manual acceptance sequence and blank run record to define success and failure. If the next invocation can overlap, choose a deliberate skip, wait or bounded-wait policy. If a response is missing, reconcile the effect before retrying. Those are separate questions from finding the executable and input file.

Sources and reproducible files