<@ULVA73B9P> prefect.deployments.steps.set_working...
# ask-marvin
j
@Marvin prefect.deployments.steps.set_working_directory does this need relative or absolute paths?
m
thought for 47.5 seconds
It accepts both.
prefect.deployments.steps.set_working_directory(directory=...)
uses Python's
os.chdir(directory)
, so you can pass either: - a relative path - an absolute path A couple of important details: - Relative paths are resolved relative to the current working directory at runtime - The step returns the absolute resolved directory - The directory must already exist, or it will error So this is valid:
Copy code
yaml
pull:
  - prefect.deployments.steps.set_working_directory:
      directory: ./my-subdir
and so is this:
Copy code
yaml
pull:
  - prefect.deployments.steps.set_working_directory:
      directory: /opt/prefect/my-subdir
If you want, I can also show how this behaves inside
prefect.yaml
pull steps specifically, since that’s usually where path confusion comes up.
j
UUID('069f8c88-3526-7d2d-8000-981825f819c5'). Traceback (most recent call last): File "<frozen importlib._bootstrap_external>", line 936, in exec_module File "<frozen importlib._bootstrap_external>", line 1073, in get_code File "<frozen importlib._bootstrap_external>", line 1130, in get_data FileNotFoundError: [Errno 2] No such file or directory: '/rn/maintenance/redshift/redshift_vacuum_cleaner.py' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/local/lib/python3.11/dist-packages/prefect/flows.py", line 2509, in load_flow_from_entrypoint flow: Flow[P, Any] = import_object(entrypoint) # pyright: ignore[reportRedeclaration] ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/prefect/utilities/importtools.py", line 157, in import_object module = load_script_as_module(script_path) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/prefect/utilities/importtools.py", line 122, in load_script_as_module raise ScriptError(user_exc=exc, path=path) from exc prefect.exceptions.ScriptError: Script at 'rn/maintenance/redshift/redshift_vacuum_cleaner.py' encountered an exception: FileNotFoundError(2, 'No such file or directory') During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/local/lib/python3.11/dist-packages/prefect/runner/runner.py", line 1548, in _run_on_crashed_hooks flow = await load_flow_from_flow_run( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/prefect/client/utilities.py", line 69, in wrapper return await func(client, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/prefect/flows.py", line 2832, in load_flow_from_flow_run flow = await run_sync_in_worker_thread( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/prefect/utilities/asyncutils.py", line 233, in run_sync_in_worker_thread result = await anyio.to_thread.run_sync( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/anyio/to_thread.py", line 56, in run_sync return await get_async_backend().run_sync_in_worker_thread( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/anyio/_backends/_asyncio.py", line 2485, in run_sync_in_worker_thread return await future ^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/anyio/_backends/_asyncio.py", line 976, in run result = context.run(func, *args) ^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/prefect/utilities/asyncutils.py", line 243, in call_with_mark return call() ^^^^^^ File "/usr/local/lib/python3.11/dist-packages/prefect/flows.py", line 2518, in load_flow_from_entrypoint flow: Optional[Flow[P, Any]] = safe_load_flow_from_entrypoint(entrypoint) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/prefect/flows.py", line 2895, in safe_load_flow_from_entrypoint func_or_cls_def, source_code, parts = _entrypoint_definition_and_source(entrypoint) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/dist-packages/prefect/flows.py", line 3168, in _entrypoint_definition_and_source source_code = Path(path).read_text() ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/pathlib.py", line 1058, in read_text with self.open(mode='r', encoding=encoding, errors=errors) as f: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.11/pathlib.py", line 1044, in open return io.open(self, mode, buffering, encoding, errors, newline) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FileNotFoundError: [Errno 2] No such file or directory: 'rn/maintenance/redshift/redshift_vacuum_cleaner.py'
@Marvin
m
thought for 8.0 seconds
Also, because this is a deployment step, I’d generally recommend absolute paths unless you’re very sure what the runtime working directory is. Why: with a relative path, behavior depends on where the worker/process starts from, which can vary across local runs, Docker, Kubernetes, etc. If you're using
prefect.yaml
, I can help you sanity-check a specific example.
j
@Marvin
Copy code
- prefect.deployments.steps.set_working_directory:
      directory: "."

definitions:
  work_pools:
    rn-prefect-worker-work-pool: &rn-prefect-worker-work-pool
      name: "rn-etl-services-work-pool-{{$env}}"
      work_queue_name: "{{$env}}"
      job_variables:
        image: "<http://384144335252.dkr.ecr.us-east-2.amazonaws.com/{{$service}}-{{$env}}:latest|384144335252.dkr.ecr.us-east-2.amazonaws.com/{{$service}}-{{$env}}:latest>"
        task_role_arn: "arn:aws:iam::384144335252:role/{{$service}}-{{$env}}-task-role"
        execution_role_arn: "arn:aws:iam::384144335252:role/{{$service}}-{{$env}}-task-exec-role"
        name: "{{$service}}-prefect-task-run-{{$env}}"

deployments:
- name: "vacuum_cleaner_{{$env}}"
  entrypoint: "rn/maintenance/redshift/redshift_vacuum_cleaner.py:vacuum_cleaner"
  work_pool:
    name: "rn-etl-services-work-pool-{{$env}}"
  version:
  tags:
    - "{{$env}}"
    - "{{$service}}"
    - "vacuum_cleaner"
    - "redshift"
  description: "Redshift maintenance task - runs vacuum and analyze statements against dw tables."
  schedules:
  - cron: "0 0 * * *"
    timezone: "America/New_York"
m
thought for 11.3 seconds
In that config,
directory: "."
means: - "change into the *current working directory*" - so it effectively does nothing useful unless the current working directory is already exactly what you want That’s usually not a good idea in deployment pull steps, because the worker’s starting directory may not be stable or obvious. A few important points for your example: 1.
entrypoint
is resolved relative to the working directory used at runtime
So with:
Copy code
yaml
entrypoint: "rn/maintenance/redshift/redshift_vacuum_cleaner.py:vacuum_cleaner"
Prefect will need the runtime working directory to be the repo/project root that contains
rn/...
. 2.
set_working_directory: "."
does not guarantee that
It just preserves whatever directory the process already started in. 3. Better options If your code is already inside the image at a known path, use an absolute path like:
Copy code
yaml
pull:
  - prefect.deployments.steps.set_working_directory:
      directory: "/opt/prefect"
or whatever directory in the container actually contains
rn/
. Then this entrypoint works predictably:
Copy code
yaml
entrypoint: "rn/maintenance/redshift/redshift_vacuum_cleaner.py:vacuum_cleaner"
4. If you are cloning/pulling code first Then set the working directory to the known checkout location, or use steps that place code in a specific directory before resolving the entrypoint. So the short answer is: -
.
is allowed - but in your config it is probably not reliable - prefer an absolute directory inside the container that contains the
rn
folder If you want, I can help you map this to your ECS image layout and suggest the exact
directory
value to use.
j
@Marvin my deployment image seems to have a different working dir than my execution image. How do I work around that?
m
thought for 121.5 seconds
Yes — this is a common gotcha. The clean workaround is: make runtime code location explicit, instead of relying on whatever working directory happened to exist during
prefect deploy
. What’s happening There are really two different environments: - the environment where you run
prefect deploy
- the environment where the worker executes the flow run Your
entrypoint
has to make sense in the execution environment, not just the deploy environment. If those two images have different working dirs, then relative file-based paths can break unless you normalize them. Best ways to work around it Option 1: Put code in a fixed path in the execution image and set that as the runtime working directory This is the most straightforward approach for Docker/ECS. For example, if your image contains your repo at
/app
, then use:
Copy code
yaml
pull:
  - prefect.deployments.steps.set_working_directory:
      directory: "/app"
and keep your entrypoint relative to that:
Copy code
yaml
entrypoint: "rn/maintenance/redshift/redshift_vacuum_cleaner.py:vacuum_cleaner"
This makes runtime deterministic, regardless of where
prefect deploy
was run from. Option 2: Make your image
WORKDIR
match where the code lives
If your Dockerfile says:
Copy code
dockerfile
WORKDIR /app
COPY . /app
then Prefect runtime behavior is much easier to reason about. In many cases you may not even need
set_working_directory
, as long as the container truly starts in
/app
. Still, explicitly setting it in
pull
can be safer if you want to avoid ambiguity. Option 3: Install your code as a Python package and use module imports If your flow code is packaged and importable, you can avoid some file path fragility by making sure the module is installed in the runtime image. This helps especially when filesystem layout differs between build/deploy and execution environments. What I would not rely on -
directory: "."
- assuming the deploy-time working directory matches runtime - absolute file entrypoints like
/app/foo.py:flow
as a general strategy In practice, Prefect expects file entrypoints to be resolved from the runtime path context, so the stable pattern is: - choose a known runtime directory -
set_working_directory
to that directory - keep
entrypoint
relative to it Recommended pattern for your case Since you're using an ECS-style image, I’d recommend: 1. Ensure your image copies the repo into a known location, e.g.
/app
2. Set container
WORKDIR /app
if possible 3. In
prefect.yaml
, use:
Copy code
yaml
pull:
  - prefect.deployments.steps.set_working_directory:
      directory: "/app"
4. Keep:
Copy code
yaml
entrypoint: "rn/maintenance/redshift/redshift_vacuum_cleaner.py:vacuum_cleaner"
That way, deploy image and execution image can differ, and Prefect still resolves the flow from the runtime location you control. CLI reference Verified command: - Prefect docs -
prefect deploy --help
If you want, send me your Dockerfile (or just
WORKDIR
+
COPY
lines), and I can tell you the exact
directory
value to use.
j
@Marvin I have tried your exact suggestion but with /src instead of /app and it does not work. IT seems like prefect unexpectedly adds new parent folders during the execution. The deployment works fine but not the executions
m
thought for 158.7 seconds
That does sound plausible, and it lines up with how Prefect execution can behave. What’s likely happening is: - your run starts in one directory - Prefect storage / pull-step handling stages code into another directory - then the final working directory used for loading the
entrypoint
is not the one you expected - so
rn/...py:vacuum_cleaner
ends up being resolved from a nested path So yes: Prefect can effectively introduce an extra parent/subdirectory layer during execution, even if
prefect deploy
succeeds. Important detail The deployment succeeding only proves Prefect could register the deployment definition. It does not prove the worker runtime can resolve:
Copy code
yaml
entrypoint: "rn/maintenance/redshift/redshift_vacuum_cleaner.py:vacuum_cleaner"
from the final execution cwd. What to do next The fastest way to work around this is to inspect the real runtime cwd and filesystem inside the execution container. If possible, add this at the top of your flow file temporarily:
Copy code
python
import os
from pathlib import Path

print("CWD:", os.getcwd())
print("FILES:", os.listdir("."))
print("SRC EXISTS:", Path("/src").exists())
if Path("/src").exists():
    print("/src:", os.listdir("/src"))
If the flow file never imports, then the issue is happening before your flow code loads, which is even more evidence that the
entrypoint
path is being resolved from the wrong directory. In that case, I’d recommend one of these more robust fixes: Option 1: Avoid file-path entrypoints by packaging your code If your code is installed into the image as a Python package, Prefect can load it by importability rather than fragile filesystem-relative lookup. This is often the most reliable fix when runtime path staging is weird. Option 2: Make the execution image contain the code at a stable path and remove pull-step path dependence If the code is already baked into the image, avoid mixing that with pull logic that changes directories unexpectedly. In other words: - either bake code into image and run from fixed image path - or fetch code dynamically with pull steps - but avoid a hybrid setup unless you know exactly how cwd lands Option 3: Adjust the entrypoint to match the actual final runtime directory For example, if Prefect leaves you in something like:
Copy code
text
/src/my-repo
then your entrypoint may need to be relative to that final directory, not
/src
. So if
rn
is actually under
/src/my-repo/rn
, then: - working directory should be
/src/my-repo
- entrypoint should remain
rn/...py:vacuum_cleaner
But if cwd is
/src
and repo is cloned into
/src/my-repo
, then the entrypoint may need to be:
Copy code
yaml
entrypoint: "my-repo/rn/maintenance/redshift/redshift_vacuum_cleaner.py:vacuum_cleaner"
My strongest suspicion in your case You may have: - image code at
/src/...
- but execution is ending up in
/src/<extra-folder>/
or - execution starts in
/src
, then Prefect clones/downloads/stages into a child dir and changes into it That would explain why deployment works but runs fail. What I need to pinpoint it Please send one of these: 1. the exact execution error / traceback 2. your full
pull:
section 3. your Dockerfile
WORKDIR
and
COPY
lines 4. the actual directory layout inside the runtime image, e.g.
Copy code
text
   /src/rn/maintenance/redshift/redshift_vacuum_cleaner.py
With that, I can tell you whether the fix is: - changing
entrypoint
- removing
set_working_directory
- changing image layout - or switching to a package/module-based setup Also, there weren’t any matching GitHub issues/discussions I could find for this exact path-nesting pattern, so I don’t want to guess beyond this without your runtime error.
j
@Marvin
Copy code
Worker 'ProcessWorker 72d06a79-d5ea-455f-8a3b-9b1bd59c9fd4' submitting flow run '069f8d30-d991-78aa-8000-e9bbd30b1c59'
Running on worker id: 99cdcffb-eec0-489d-8671-a62395e20028. See worker logs here: <https://app.prefect.cloud/account/a477ad0f-3014-426b-b1cb-48639ac6ac5b/workspace/0204ca3a-2901-4601-bf6e-c9906cdf2107/work-pools/work-pool/rn-etl-services-work-pool-dev/worker/99cdcffb-eec0-489d-8671-a62395e20028>
Opening process...
Completed submission of flow run '069f8d30-d991-78aa-8000-e9bbd30b1c59'
 > Running set_working_directory step...
[Errno 2] No such file or directory: '/src'
Unexpected exception encountered when trying to load flow
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/dist-packages/prefect/deployments/steps/core.py", line 166, in run_steps
    step_output = await run_step(step, upstream_outputs)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/deployments/steps/core.py", line 137, in run_step
    result = await from_async.call_soon_in_new_thread(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/_internal/concurrency/calls.py", line 374, in aresult
    return await asyncio.wrap_future(self.future)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/_internal/concurrency/calls.py", line 399, in _run_sync
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/deployments/steps/pull.py", line 35, in set_working_directory
    os.chdir(directory)
FileNotFoundError: [Errno 2] No such file or directory: '/src'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/dist-packages/prefect/engine.py", line 112, in <module>
    flow: "Flow[..., Any]" = load_flow(flow_run)
                             ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/flow_engine.py", line 150, in load_flow
    flow = run_coro_as_sync(
           ^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/utilities/asyncutils.py", line 207, in run_coro_as_sync
    return call.result()
           ^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/_internal/concurrency/calls.py", line 365, in result
    return self.future.result(timeout=timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/_internal/concurrency/calls.py", line 192, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/local/lib/python3.11/dist-packages/prefect/_internal/concurrency/calls.py", line 441, in _run_async
    result = await coro
             ^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/utilities/asyncutils.py", line 188, in coroutine_wrapper
    return await task
           ^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/client/utilities.py", line 69, in wrapper
    return await func(client, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/flows.py", line 2816, in load_flow_from_flow_run
    output = await run_steps(
             ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/prefect/deployments/steps/core.py", line 194, in run_steps
    raise StepExecutionError(f"Encountered error while running {fqn}") from exc
prefect.deployments.steps.core.StepExecutionError: Encountered error while running prefect.deployments.steps.set_working_directory
Copy code
pull:
  - prefect.deployments.steps.set_working_directory:
      directory: "/src"

definitions:
  work_pools:
    rn-prefect-worker-work-pool: &rn-prefect-worker-work-pool
      name: "rn-etl-services-work-pool-{{$env}}"
      work_queue_name: "{{$env}}"
      job_variables:
        image: "384144335252.dkr.ecr.us-east-2.amazonaws.com/{{$service}}-{{$env}}:latest"
        task_role_arn: "arn:aws:iam::384144335252:role/{{$service}}-{{$env}}-task-role"
        execution_role_arn: "arn:aws:iam::384144335252:role/{{$service}}-{{$env}}-task-exec-role"
        name: "{{$service}}-prefect-task-run-{{$env}}"

deployments:
- name: "vacuum_cleaner_{{$env}}"
  entrypoint: "rn/maintenance/redshift/redshift_vacuum_cleaner.py:vacuum_cleaner"
  work_pool:
    name: "rn-etl-services-work-pool-{{$env}}"
  version:
  tags:
    - "{{$env}}"
    - "{{$service}}"
    - "vacuum_cleaner"
    - "redshift"
  description: "Redshift maintenance task - runs vacuum and analyze statements against dw tables."
  schedules:
  - cron: "0 0 * * *"
    timezone: "America/New_York"
m
thought for 24.2 seconds
Thanks — this log makes the issue much clearer. The immediate problem is simply:
/src
does not exist in the execution environment.
This line is the failure:
Copy code
text
[Errno 2] No such file or directory: '/src'
So Prefect is not inventing
/src
here — it’s trying to honor your pull step exactly, and the runtime container/process does not have that directory. Key takeaway Your deployment environment may have
/src
, but your execution environment does not. That means one of these is true: - the image used for the actual flow run is not the image you think it is - the image is correct, but code is copied somewhere other than
/src
- the process worker is launching in an environment where your filesystem layout differs from the built image - your Dockerfile does not actually create
/src
in the final runtime stage Why deployment still works Because
prefect deploy
only registers metadata. The failing part happens later, at flow-run execution time, when Prefect tries to run:
Copy code
yaml
pull:
  - prefect.deployments.steps.set_working_directory:
      directory: "/src"
and
/src
is missing. What this means for your current config This config is only valid if the execution image definitely contains:
Copy code
text
/src/rn/maintenance/redshift/redshift_vacuum_cleaner.py
Right now, it doesn’t. How to fix it You need to align the
pull
working directory with the actual location of the code inside the runtime container. For example, if your code is really at
/app
, use:
Copy code
yaml
pull:
  - prefect.deployments.steps.set_working_directory:
      directory: "/app"
If your code is at
/opt/prefect
, use that instead. Best next step: verify the runtime image layout Check your Dockerfile for lines like:
Copy code
dockerfile
WORKDIR /src
COPY . /src
or maybe:
Copy code
dockerfile
WORKDIR /app
COPY . /app
or multi-stage build logic where the final stage does not copy source into
/src
. A very common issue is a multistage Docker build like:
Copy code
dockerfile
FROM builder as build
WORKDIR /src
COPY . /src
...

FROM python:3.11-slim
COPY --from=build /something /something
In that case,
/src
existed in the builder stage but not the final runtime image. What I suspect most You’re probably using an image where: -
prefect deploy
or local development sees
/src
- but the ECS task image actually does not have
/src
in the final stage So the fix is probably in the Dockerfile, not in Prefect. How to confirm quickly Run the exact image locally and inspect it:
Copy code
bash
docker run --rm -it <http://384144335252.dkr.ecr.us-east-2.amazonaws.com/<service>-<env>:latest|384144335252.dkr.ecr.us-east-2.amazonaws.com/<service>-<env>:latest> sh
pwd
ls /
find / -maxdepth 3 -type f | grep redshift_vacuum_cleaner.py
That will tell you the real path of the flow file in the runtime image. Then set: -
directory
to the parent folder containing
rn
- keep
entrypoint
relative to that directory So if the file is found at:
Copy code
text
/app/rn/maintenance/redshift/redshift_vacuum_cleaner.py
then use:
Copy code
yaml
pull:
  - prefect.deployments.steps.set_working_directory:
      directory: "/app"

entrypoint: "rn/maintenance/redshift/redshift_vacuum_cleaner.py:vacuum_cleaner"
One more important note Because your log says:
Copy code
text
Worker 'ProcessWorker ...' submitting flow run ...
Opening process...
this is a process worker execution path. That means the run is being launched as a process from the worker environment, and the path must exist in that runtime environment. If the worker is not actually running inside the image containing your code, the
image
job variable may not be taking effect the way you expect. That’s a very important clue. A plain process worker runs subprocesses on the worker host. It does not automatically mean "run inside this Docker/ECS image" unless the underlying work pool type actually uses containerized infrastructure. So I’d verify your work pool type first. What to check right now
1. Is
rn-etl-services-work-pool-{{$env}}
an ECS work pool or a process work pool? 2. Does the worker host itself contain
/src
? 3. Is the
image
job variable actually used by that pool type? If this is truly a process pool, then
image:
is likely irrelevant, and your flow is running on the worker machine filesystem, not in your container image. That would fully explain everything. If you want, paste the output of: -
prefect work-pool inspect rn-etl-services-work-pool-dev
- or a screenshot of the work pool type in Cloud and I can tell you whether the
image
setting is being ignored.
j
@Marvin 1. ecs 2. yes that is the working dir set in the docker file (shared below) 3. yes docker image
Copy code
FROM public.ecr.aws/docker/library/python:3.11-slim-bookworm


RUN python -m pip install --upgrade pip
RUN python -m pip install --upgrade setuptools

WORKDIR /src
COPY . .

RUN pip install --no-cache-dir -r requirements.txt
RUN pip install .
Copy code
(venv) RELAY-435-MBPr:rn-etl-services jrose$ prefect work-pool inspect rn-etl-services-work-pool-dev
WorkPool(
    id='82157a01-d48e-4194-bf3d-f93eee762350',
    created=DateTime(2025, 8, 20, 16, 53, 58, 557748, tzinfo=Timezone('UTC')),
    updated=DateTime(2026, 2, 21, 21, 3, 47, 519960, tzinfo=Timezone('UTC')),
    name='rn-etl-services-work-pool-dev',
    type='process',
    base_job_template={
        'variables': {
            'type': 'object',
            'properties': {
                'env': {
                    'type': 'object',
                    'title': 'Environment Variables',
                    'description': 'Environment variables to set when starting a flow run.',
                    'additionalProperties': {'anyOf': [{'type': 'string'}, {'type': 'null'}]}
                },
                'name': {
                    'anyOf': [{'type': 'string'}, {'type': 'null'}],
                    'title': 'Name',
                    'default': None,
                    'description': 'Name given to infrastructure created by a worker.'
                },
                'labels': {
                    'type': 'object',
                    'title': 'Labels',
                    'description': 'Labels applied to infrastructure created by a worker.',
                    'additionalProperties': {'type': 'string'}
                },
                'command': {
                    'anyOf': [{'type': 'string'}, {'type': 'null'}],
                    'title': 'Command',
                    'default': None,
                    'description': 'The command to use when starting a flow run. In most cases, this should be left blank and the command will be
automatically generated by the worker.'
                },
                'working_dir': {
                    'anyOf': [{'type': 'string', 'format': 'path'}, {'type': 'null'}],
                    'title': 'Working Directory',
                    'default': None,
                    'description': 'If provided, workers will open flow run processes within the specified path as the working directory. Otherwise,
a temporary directory will be created.'
                },
                'stream_output': {
                    'type': 'boolean',
                    'title': 'Stream Output',
                    'default': True,
                    'description': 'If enabled, workers will stream output from flow run processes to local standard output.'
                }
            }
        },
        'job_configuration': {
            'env': '{{ env }}',
            'name': '{{ name }}',
            'labels': '{{ labels }}',
            'command': '{{ command }}',
            'working_dir': '{{ working_dir }}',
            'stream_output': '{{ stream_output }}'
        }
    },
    status=WorkPoolStatus.READY,
    storage_configuration=WorkPoolStorageConfiguration(),
    default_queue_id='779426c7-0ec5-49e7-a55b-62cd4c261fcc'
)
m
thought for 16.9 seconds
Aha — this explains it completely. Your work pool is not ECS. It is:
Copy code
text
type='process'
So the root issue is: your deployment is running on a process work pool, not an ECS work pool. That means: - the flow run is executed as a local subprocess on the worker machine - the
image
job variable is ignored - your Dockerfile and container filesystem are not what the run is using -
/src
must exist on the worker host, not in your Docker image So when you said “it’s ECS”, the actual inspected pool says otherwise. Prefect is trusting the pool type, and the pool type is definitely
process
. That also explains this earlier log line:
Copy code
text
Worker 'ProcessWorker ...'
Opening process...
That was the clue. Why your
image
setting isn’t helping
In a
process
work pool, valid job variables are things like: -
env
-
name
-
labels
-
command
-
working_dir
-
stream_output
There is no container image execution here. So this section in your deployment:
Copy code
yaml
job_variables:
  image: "<http://384144335252.dkr.ecr.us-east-2.amazonaws.com/{{$service}}-{{$env}}:latest|384144335252.dkr.ecr.us-east-2.amazonaws.com/{{$service}}-{{$env}}:latest>"
  task_role_arn: ...
  execution_role_arn: ...
does not apply to a process pool. Those values are basically not meaningful for this pool type. What you should do You have two paths: Option 1: Use a real ECS work pool If you want Prefect to run flow runs in your Docker image on ECS, you need an ECS work pool, not a process work pool. Then your
image
,
task_role_arn
, and
execution_role_arn
fields will make sense. Right now they do not, because the pool type is wrong for that configuration. Option 2: Stay with a process pool Then stop thinking in terms of Docker image layout. Your code must exist on the worker host, and paths must be host paths. In that case,
/src
would need to exist on the host where the process worker is running. But from your goal, it sounds like this is not what you want. My recommendation You should switch to an ECS work pool. Because your deployment config is clearly written for ECS-style execution: -
image
-
task_role_arn
-
execution_role_arn
That config belongs on an ECS work pool, not a process pool. Why the current behavior feels inconsistent Deployment registration works because Prefect accepts the deployment metadata. Execution fails or behaves strangely because the actual worker type is not the infrastructure type you intended. So the problem is not really
set_working_directory
. The bigger problem is: the deployment is targeting the wrong work pool type. What to do next 1. Create or use an ECS work pool 2. Point this deployment at that ECS work pool 3. Then your image filesystem matters, and
/src
should be valid if the image is built as shown 4. Remove any assumptions based on the current
process
pool Useful self-serve paths - Work pools docs: https://docs.prefect.io/ - Cloud UI: Prefect Cloud If you want, I can help you rewrite this deployment block for a proper ECS work pool setup.