Nikhil Jain
04/02/2026, 6:23 PMdef _build_artifact_markdown(plan_year):
return f"## Sample artifact\n Received: {plan_year}"
@flow
async def my_flow(plan_year):
# do some work
# ...
# Create Prefect artifact
environment = os.environ.get('FLASK_ENV', 'prod')
artifact_key = f'{environment}-cms-plan-validation-{plan_year}'
markdown = _build_artifact_markdown(plan_year)
create_markdown_artifact(
key=artifact_key,
markdown=markdown,
description=f'CMS plan validation report for {plan_year}',
)
<http://logger.info|logger.info>(f'Created artifact with key: {artifact_key}')Marvin
04/02/2026, 6:23 PMMarvin
04/02/2026, 6:26 PMcreate_markdown_artifact is the correct function and import, and you do not need to await it even in an async flow. It returns a UUID you can log.
What to verify in your code
- Import and usage:
from prefect import flow
from prefect.artifacts import create_markdown_artifact
def _build_artifact_markdown(plan_year):
return f"## Sample artifact\n Received: {plan_year}"
@flow
async def my_flow(plan_year: int):
environment = os.environ.get("FLASK_ENV", "prod")
artifact_key = f"{environment}-cms-plan-validation-{plan_year}"
markdown = _build_artifact_markdown(plan_year)
artifact_id = create_markdown_artifact(
key=artifact_key,
markdown=markdown,
description=f"CMS plan validation report for {plan_year}",
)
<http://logger.info|logger.info>(f"Created artifact with id={artifact_id} key={artifact_key}")
- Don’t add await to create_markdown_artifact (it’s sync with async-dispatch under the hood).
If you’re not seeing the artifact, it’s usually one of these:
1) You’re on ephemeral mode (default) and there’s no UI
- The artifact is likely created in the ephemeral API but you can’t view it anywhere.
- To confirm creation, set PREFECT_LOGGING_LEVEL=DEBUG and re-run; you’ll see Prefect start an ephemeral server and the artifact POST.
- To see artifacts in a UI locally:
- Start a local server: prefect server start
- Point your run to it:
- macOS/Linux:
export PREFECT_API_URL=<http://127.0.0.1:4200/api>
- Windows PowerShell:
$env:PREFECT_API_URL = "<http://127.0.0.1:4200/api>"
- Run your flow again, then open http://127.0.0.1:4200 and check the flow run’s Artifacts tab.
2) Ephemeral API is disabled or API isn’t reachable
- Ensure you did not set PREFECT_SERVER_EPHEMERAL_ENABLED=false.
- If using Prefect Cloud, make sure both PREFECT_API_URL (your workspace URL) and PREFECT_API_KEY are set; otherwise the client call will fail silently or log a warning.
- Look for warnings like “Could not reach Prefect API” in your logs.
3) Expectation mismatch about where the artifact should appear
- In Prefect Cloud: you’ll see it on the flow run page under the Artifacts tab.
- In local ephemeral mode: there’s no UI; use a local server if you want to view it.
Helpful links
- Artifacts concepts
- How to produce artifacts
- Server and ephemeral settings
A couple of quick questions to help pinpoint this:
- Where are you expecting to see the artifact (Cloud UI, local UI, or just logs)?
- Which Prefect version are you on?
- Do your logs show any warnings about the API or ephemeral server? If you can share those lines, I can interpret them.Nikhil Jain
04/02/2026, 6:39 PM/Users/nikhiljain/Code/artemis/server/flows/auto_qa/cms_plan_validation_flow.py:149: RuntimeWarning: coroutine 'acreate_markdown_artifact' was never awaited
create_markdown_artifact(
This indicates that calling create_markdown_artifact() does not work in an async flow?
I changed it to await acreate_markdown_artifact() and now I can see the markdown created. Is this not the correct usage though? Can I directly call await create_markdown_artifact() ? @MarvinMarvin
04/02/2026, 7:03 PMMarvin
04/02/2026, 7:05 PMartifact_id = await create_markdown_artifact(...)
- artifact_id = await acreate_markdown_artifact(...)
- In a sync flow/task, call the sync name without await:
- artifact_id = create_markdown_artifact(...)
Why you saw “coroutine was never awaited”
- create_markdown_artifact is decorated with an async-dispatch wrapper. In an async context (like your @flow async def ...), it returns a coroutine that you need to await. If you don’t, you’ll get exactly the warning you saw.
- In a sync context, it runs synchronously and returns a UUID directly.
Examples
- Async flow:
from prefect import flow
from prefect.artifacts import create_markdown_artifact # or acreate_markdown_artifact
@flow
async def my_flow(plan_year: int):
artifact_id = await create_markdown_artifact(
key=f"prod-cms-plan-validation-{plan_year}",
markdown=f"## Sample artifact\n Received: {plan_year}",
description=f"CMS plan validation report for {plan_year}",
)
<http://logger.info|logger.info>(f"Created artifact {artifact_id}")
- Sync flow:
from prefect import flow
from prefect.artifacts import create_markdown_artifact
@flow
def my_flow(plan_year: int):
artifact_id = create_markdown_artifact(
key=f"prod-cms-plan-validation-{plan_year}",
markdown=f"## Sample artifact\n Received: {plan_year}",
description=f"CMS plan validation report for {plan_year}",
)
<http://logger.info|logger.info>(f"Created artifact {artifact_id}")
References
- How to produce artifacts
- Artifacts module source
- Async-dispatch implementation
Since you’re connected to Prefect Cloud (non-ephemeral), you should see the artifact on your flow run page’s Artifacts tab after awaiting the call.
If you want a single import that works in both sync and async code, prefer create_markdown_artifact and:
- await it in async code
- call it normally in sync code