You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
JobRunner._finish_or_resubmit_job opens outputs/tool_stdout unguarded. If a job dies before that file is written, the resulting FileNotFoundError is re-raised, the caller turns it into job_wrapper.fail("Unable to finish job", exception=True), and everything the runner did know about the failure is discarded. The job lands with exit_code = NULL, no tool_stderr, no job_stderr and no job_messages.
try:
withopen(tool_stdout_path, "rb") asstdout_file:
tool_stdout=self._job_io_for_db(stdout_file)
withopen(tool_stderr_path, "rb") asstderr_file:
tool_stderr=self._job_io_for_db(stderr_file)
exceptFileNotFoundError:
ifjob.statein (model.Job.states.DELETING, model.Job.states.DELETED):
# We killed the job, so we may not even have the tool stdout / tool stderrtool_stdout=""tool_stderr="Job cancelled"else:
# Should we instead just move on ?# In the end the only consequence here is that we won't be able to determine# if the job failed for known tool reasons (check_tool_output).# OTOH I don't know if this can even be reached# Deal with it if we ever get reports about this.raise
This is that report. The branch is reachable, and not rarely.
Observed
A single ~1 hour window on one deployment (2163 jobs) produced 14 occurrences, on two different runners:
runner
tool
count
exit_code IS NULL
no stderr at all
k8s
mgnify_seqprep
6
6
6
local
__DATA_FETCH__
8
8
8
Every one logs the same thing:
galaxy.jobs.runners ERROR (227/25156) Job wrapper finish method failed
Traceback (most recent call last):
File "/galaxy/server/lib/galaxy/jobs/runners/__init__.py", line 655, in _finish_or_resubmit_job
with open(tool_stdout_path, "rb") as stdout_file:
FileNotFoundError: [Errno 2] No such file or directory:
'/galaxy/server/database/jobs_directory/000/227/outputs/tool_stdout'
and every one is stored as:
info = "Unable to finish job"
exit_code = NULL
tool_stderr / job_stderr / job_messages = empty
Why this is worth changing
The two groups above have completely different underlying causes, and are indistinguishable in Galaxy:
mgnify_seqprep (k8s).job_state_history is new → queued → error; the job never reaches running. The container never starts, because the image it requests has no /bin/bash while the runner sets "command": [ajs.job_wrapper.shell]. The real reason is sitting in the Kubernetes event:
Error: failed to create containerd task: ... runc create failed:
unable to start container process: error during container init:
exec: "/bin/bash": stat /bin/bash: no such file or directory
Recoverable via kubectl get events, not from Galaxy.
__DATA_FETCH__ (local). These do reach running and run to completion. outputs/tool_stdout is created empty at job start by the plain shell redirect (stream_stdout_stderr is off), so it existed and then went away. Cause still unknown — but the raise guarantees no diagnostic survives to help.
So an infrastructure problem, a bad tool container and (presumably) a filesystem problem all render as the same opaque Unable to finish job. That is what makes this expensive: the message is identical no matter what actually went wrong, so triage has to start from the handler log, and only if it hasn't rotated.
Worth noting retry_job_output_collection does not help — the failing read happens in _finish_or_resubmit_job, before job_wrapper.finish(), so the retry never wraps it.
Suggested fix
The alternative the comment itself proposes — "just move on" — would be a large improvement. Treating a missing tool_stdout as empty and continuing lets check_tool_output run against the exit code and the runner's own state, so the job fails with whatever the runner actually knew instead of with a swallowed FileNotFoundError. If the file's absence is itself worth surfacing, recording it in job_messages would keep the signal without destroying the rest.
Environment
Galaxy 26.1.1 (quay.io/galaxyproject/galaxy-min:26.1-auto), deployed via galaxy-helm on Kubernetes
Job working directories on NFS
Reproduced across local and kubernetes runners; the same shape was present in an earlier run a day before
Related
k8s runner not collecting logs for failed job #8146 — older, k8s-specific report of the same user-visible symptom ("Unable to finish job", logs not collected). Quite possibly the same root cause seen through one runner; this issue is the general form, since the local runner hits it too.
Summary
JobRunner._finish_or_resubmit_jobopensoutputs/tool_stdoutunguarded. If a job dies before that file is written, the resultingFileNotFoundErroris re-raised, the caller turns it intojob_wrapper.fail("Unable to finish job", exception=True), and everything the runner did know about the failure is discarded. The job lands withexit_code = NULL, notool_stderr, nojob_stderrand nojob_messages.The code anticipates this and asks for a report —
lib/galaxy/jobs/runners/__init__.py#L653-L669ondev:This is that report. The branch is reachable, and not rarely.
Observed
A single ~1 hour window on one deployment (2163 jobs) produced 14 occurrences, on two different runners:
exit_code IS NULLk8smgnify_seqpreplocal__DATA_FETCH__Every one logs the same thing:
and every one is stored as:
Why this is worth changing
The two groups above have completely different underlying causes, and are indistinguishable in Galaxy:
mgnify_seqprep(k8s).job_state_historyisnew → queued → error; the job never reachesrunning. The container never starts, because the image it requests has no/bin/bashwhile the runner sets"command": [ajs.job_wrapper.shell]. The real reason is sitting in the Kubernetes event:Recoverable via
kubectl get events, not from Galaxy.__DATA_FETCH__(local). These do reachrunningand run to completion.outputs/tool_stdoutis created empty at job start by the plain shell redirect (stream_stdout_stderris off), so it existed and then went away. Cause still unknown — but theraiseguarantees no diagnostic survives to help.So an infrastructure problem, a bad tool container and (presumably) a filesystem problem all render as the same opaque
Unable to finish job. That is what makes this expensive: the message is identical no matter what actually went wrong, so triage has to start from the handler log, and only if it hasn't rotated.Worth noting
retry_job_output_collectiondoes not help — the failing read happens in_finish_or_resubmit_job, beforejob_wrapper.finish(), so the retry never wraps it.Suggested fix
The alternative the comment itself proposes — "just move on" — would be a large improvement. Treating a missing
tool_stdoutas empty and continuing letscheck_tool_outputrun against the exit code and the runner's own state, so the job fails with whatever the runner actually knew instead of with a swallowedFileNotFoundError. If the file's absence is itself worth surfacing, recording it injob_messageswould keep the signal without destroying the rest.Environment
quay.io/galaxyproject/galaxy-min:26.1-auto), deployed via galaxy-helm on Kuberneteslocalandkubernetesrunners; the same shape was present in an earlier run a day beforeRelated
localrunner hits it too.