<@ULVA73B9P> I want to use functions (prefect task...
# marvin-ai
s
@Marvin I want to use functions (prefect tasks) without needed to connect to the prefect api. Is this possible and how ?
m
thought for 191.2 seconds
Yes — in Prefect 3.x you can use tasks (and flows) entirely locally without connecting to any API. What this means in practice - If you don’t configure an API, Prefect runs flows/tasks in-process with no network calls. - You can still use logging, retries, caching, and task runners locally; Cloud-only features (artifacts, events, automations, UI) won’t work offline. - If you want to be extra sure no telemetry is sent, disable it with an environment variable. How to run offline 1) Don’t set any API credentials - Ensure
PREFECT_API_URL
and
PREFECT_API_KEY
are unset in your environment. 2) Disable telemetry (optional, but recommended for air‑gapped/offline) - Bash:
Copy code
export PREFECT_DISABLE_TELEMETRY=1
  unset PREFECT_API_URL
  unset PREFECT_API_KEY
- PowerShell:
Copy code
$env:PREFECT_DISABLE_TELEMETRY="1"
  $env:PREFECT_API_URL=$null
  $env:PREFECT_API_KEY=$null
3) Write and run your tasks/flows as normal
Copy code
from prefect import task, flow, get_run_logger
from prefect.task_runners import ThreadPoolTaskRunner

@task
def add(a: int, b: int) -> int:
    return a + b

@flow(task_runner=ThreadPoolTaskRunner())
def my_flow():
    logger = get_run_logger()
    result = add(1, 2)  # runs locally, no API
    <http://logger.info|logger.info>(f"Result: {result}")
    return result

if __name__ == "__main__":
    my_flow()
Notes and clarifications - Calling a task inside a flow uses Prefect’s engine (retries, caching, timeouts, etc.) but still runs purely locally if you haven’t set an API. - Calling a task outside a flow behaves like a normal Python function; orchestration features apply only within a flow run. - Avoid Cloud-only helpers (e.g., creating artifacts or emitting events) when offline, as they require an API. Docs and references - Settings reference (env vars like
PREFECT_API_URL
,
PREFECT_DISABLE_TELEMETRY
): Prefect 3 Settings reference - Connecting to Cloud (for contrast): Connect to Cloud - Prefect 3 GA overview (local-first design): Prefect 3 GA blog Are you on Prefect 3.x? If you’re on 2.x, the answer is still “yes” for local use, but the ergonomics differ slightly — happy to tailor the steps for your version.
s
@Marvin That doesn't seem to work. I unset the env vars and:
Copy code
Traceback (most recent call last):
  File 
"/opt/homebrew/lib/python3.13/site-packages/marimo/_runtime/executor.py"
, line 138, in execute_cell
    exec(cell.body, glbls)
    ~~~~^^^^^^^^^^^^^^^^^^
  Cell <marimo://v2/dev.py#cell=cell-23>
, line 3, in <module>
    error_df = fetch_measurements_for_user_day(error_uid, error_date)
  File 
"/opt/homebrew/lib/python3.13/site-packages/prefect/tasks.py"
, line 1002, in __call__
    return run_task(
        task=self,
    ...<2 lines>...
        return_type=return_type,
    )
  File 
"/opt/homebrew/lib/python3.13/site-packages/prefect/task_engine.py"
, line 1526, in run_task
    return run_task_sync(**kwargs)
  File 
"/opt/homebrew/lib/python3.13/site-packages/prefect/task_engine.py"
, line 1333, in run_task_sync
    with engine.start(task_run_id=task_run_id, dependencies=dependencies):
         ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File 
"/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py"
, line 141, in __enter__
    return next(self.gen)
  File 
"/opt/homebrew/lib/python3.13/site-packages/prefect/task_engine.py"
, line 732, in start
    with self.initialize_run(task_run_id=task_run_id, dependencies=dependencies):
         ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File 
"/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py"
, line 141, in __enter__
    return next(self.gen)
  File 
"/opt/homebrew/lib/python3.13/site-packages/prefect/task_engine.py"
, line 655, in initialize_run
    with SyncClientContext.get_or_create() as client_ctx:
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File 
"/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py"
, line 141, in __enter__
    return next(self.gen)
  File 
"/opt/homebrew/lib/python3.13/site-packages/prefect/context.py"
, line 236, in get_or_create
    with SyncClientContext() as ctx:
         ~~~~~~~~~~~~~~~~~^^
  File 
"/opt/homebrew/lib/python3.13/site-packages/prefect/context.py"
, line 218, in __enter__
    self.client.raise_for_api_version_mismatch()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File 
"/opt/homebrew/lib/python3.13/site-packages/prefect/client/orchestration.py"
, line 3736, in raise_for_api_version_mismatch
    raise RuntimeError(f"Failed to reach API at {self.api_url}") from e
RuntimeError: Failed to reach API at <http://127.0.0.1:4200/api/>
m
thought for 64.9 seconds
Thanks for the traceback — that helps. Prefect is still trying to reach an API at http://127.0.0.1:4200/api, which means your Prefect profile still has an API URL set even if you unset the environment variables. What to do 1) Check your current Prefect settings - Run:
Copy code
prefect config view
- If you see
PREFECT_API_URL = "<http://127.0.0.1:4200/api>"
(or any URL), that’s the culprit. 2) Unset the API settings from your profile - Run:
Copy code
prefect config unset PREFECT_API_URL PREFECT_API_KEY PREFECT_UI_URL
prefect config validate
prefect config view
- After this,
PREFECT_API_URL
should be empty/absent. Prefect will then run locally with no API calls. 3) (Optional) Force offline in code for notebooks/cells - If you want to be absolutely sure (or temporarily override a profile), wrap your call in `temporary_settings`:
Copy code
from prefect.settings import temporary_settings, PREFECT_API_URL, PREFECT_API_KEY, PREFECT_DISABLE_TELEMETRY

with temporary_settings({
    PREFECT_API_URL: None,
    PREFECT_API_KEY: None,
    PREFECT_DISABLE_TELEMETRY: True,  # optional
}):
    # call your Prefect task/flow here
    error_df = fetch_measurements_for_user_day(error_uid, error_date)
A few notes - Calling a task outside a flow is fine and should still be local/offline as long as no API URL is configured. - If your task uses Cloud-only features (e.g., artifacts/events/variables) those will try to hit the API; avoid those calls when offline. References - Settings and profiles (env vars like
PREFECT_API_URL
,
PREFECT_DISABLE_TELEMETRY
): Prefect 3 Settings reference If this still fails, could you share: - The output of
prefect version
- The lines containing
PREFECT_API_*
from
prefect config view
(no keys), so we can confirm the profile isn’t overriding your env.
s
@Marvin
Copy code
ValueError: No Prefect API URL provided. Please set PREFECT_API_URL to the address of a running Prefect server.
m
thought for 161.5 seconds
Got it — that error happens when Prefect is not allowed to start its local “ephemeral” API and you’re calling a Prefect task (which initializes a client). There are two good ways forward depending on what “no API” should mean for you. Option A: Zero API, just run the Python function - Keep your business logic as a plain function and only wrap it as a task for when you run inside a flow. Call the plain function directly when you don’t want any Prefect orchestration. Example:
Copy code
# write your logic as a plain function
def _fetch_measurements_for_user_day(uid, date):
    ...

# wrap it as a task for use in flows
from prefect import task, flow

fetch_measurements_for_user_day = task(_fetch_measurements_for_user_day)

# 1) PURE PYTHON (no Prefect engine, no API)
error_df = _fetch_measurements_for_user_day(error_uid, error_date)

# 2) PREFECT (inside a flow; see Option B if you want this offline)
@flow
def my_flow(uid, date):
    return fetch_measurements_for_user_day(uid, date)

# my_flow(...)  # runs with Prefect’s engine
Tip: If you already decorated the function with
@task
, you can either refactor as above or call the original undecorated function you factored out. This avoids any Prefect engine/client entirely. Option B: Use Prefect’s engine locally without an external server - Enable Prefect’s ephemeral server so Prefect can orchestrate locally without you running a separate server or connecting to Cloud. - Then call your task from inside a
@flow
. 1) Ensure ephemeral mode is allowed and no API URL is set:
Copy code
prefect config set PREFECT_SERVER_ALLOW_EPHEMERAL_MODE=true
prefect config unset PREFECT_API_URL PREFECT_API_KEY PREFECT_UI_URL
prefect config view
You should not see an API URL. Ephemeral mode being true lets Prefect start a local in-process API when needed. 2) Run your flow:
Copy code
from prefect import task, flow

@task
def fetch_measurements_for_user_day(uid, date):
    ...

@flow
def run_once(uid, date):
    return fetch_measurements_for_user_day(uid, date)

run_once(error_uid, error_date)
3) If you want to force this just in a cell/session, you can set it temporarily:
Copy code
import os
os.environ["PREFECT_SERVER_ALLOW_EPHEMERAL_MODE"] = "true"
os.environ.pop("PREFECT_API_URL", None)
os.environ.pop("PREFECT_API_KEY", None)
Why you saw “No Prefect API URL provided” - When a Prefect task is invoked, the engine creates a client. If there is no API URL and ephemeral mode is disabled, Prefect raises that error. This is expected behavior; enabling ephemeral mode fixes it, or bypassing the engine (Option A) avoids any client creation at all. - Reference showing the error and ephemeral logic: GitHub issue 15818 If you truly never want any API overhead for these calls, Option A is the most direct: call the plain function (or the underlying function you factored out) instead of the
@task
object. If you’re okay with a temporary local API that Prefect spins up automatically, turn on ephemeral mode (Option B). If it persists, can you share: -
prefect version
- The relevant lines from
prefect config view
for
PREFECT_API_URL
and
PREFECT_SERVER_ALLOW_EPHEMERAL_MODE
(no secrets)?