Preston Sharpe
03/09/2026, 3:03 PMFinished in state Completed()Marvin
03/09/2026, 3:03 PMMarvin
03/09/2026, 3:05 PMlogging.yaml configuration, preventing duplicate handlers, and setting appropriate levels.
- Investigation into Duplicate Logs in Prefect 3.x UI — explores common causes of duplicate/noisy logs and configuration patterns to mitigate them.
- Logging and Materialization in Prefect 3 — covers logging best practices alongside materialization; touches on controlling verbosity.
Would you like me to:
- Broaden the search to GitHub Discussions/Issues as well?
- Look for examples of per-run/per-flow dynamic level changes (e.g., setting PREFECT_LOGGING_LEVEL vs. programmatically adjusting loggers/filters at runtime)?
- Or are you targeting suppression specifically in worker stdout vs. hiding these entries from the Prefect UI?
If you share which environment you’re on (assuming Prefect 3.x) and whether you want a global or per-run solution, I can dig up or draft a concrete snippet to suppress that “Finished in state Completed()” line.Preston Sharpe
03/09/2026, 3:13 PMPrefectDbtRunner and I have one task that essentially executes dbt compile because subsequent tasks parse the manifest.
During the dbt compile task -- each model generates its own task (I believe) so the logging surfaces a Finished in state Completed() message for each model. Is there a way to suppress those messages for just that dbt compile task and its "sub-tasks". Apologies if the terminology for "sub-task" is incorrectMarvin
03/09/2026, 3:13 PMMarvin
03/09/2026, 3:17 PMprefect.engine) for every task run. PrefectDbtRunner turns each dbt node (model/seed/etc.) into its own Prefect task during compile, so you’ll see one of those messages per node.
If you want to suppress them only during your dbt compile step (and keep normal logging elsewhere), wrap the compile call and temporarily quiet the prefect.engine logger. Here are a few approaches in Prefect 3.x:
Option A: Hide only INFO “Finished in state …” lines but still show errors
- Temporarily set prefect.engine to ERROR around your compile call.
import logging
from contextlib import contextmanager
from prefect import flow, task
from prefect_dbt import PrefectDbtRunner
@contextmanager
def temporarily_set_logger_level(name, level):
logger = logging.getLogger(name)
old = logger.level
logger.setLevel(level)
try:
yield
finally:
logger.setLevel(old)
@task
def dbt_compile_quiet():
with temporarily_set_logger_level("prefect.engine", logging.ERROR):
PrefectDbtRunner().invoke(["compile"])
@flow
def run():
dbt_compile_quiet()
# other steps run with normal logging
Option B: Filter just the “Finished in state …” records (keeps other engine logs)
import logging
from prefect import flow, task
from prefect_dbt import PrefectDbtRunner
class SuppressEngineFinishedFilter(logging.Filter):
def filter(self, record):
# Drop only the engine’s “Finished in state …” lines
return not (record.name == "prefect.engine" and "Finished in state" in record.getMessage())
@task
def dbt_compile_quiet():
engine_logger = logging.getLogger("prefect.engine")
f = SuppressEngineFinishedFilter()
engine_logger.addFilter(f)
try:
PrefectDbtRunner().invoke(["compile"])
finally:
engine_logger.removeFilter(f)
@flow
def run():
dbt_compile_quiet()
Option C: Mute the engine logger entirely during compile
- Uses Prefect’s built-in context helper to disable a logger by name.
from prefect import flow, task
from prefect.logging.loggers import disable_logger
from prefect_dbt import PrefectDbtRunner
@task
def dbt_compile_silent():
with disable_logger("prefect.engine"):
PrefectDbtRunner().invoke(["compile"])
@flow
def run():
dbt_compile_silent()
Notes and tradeoffs
- These changes are process-wide while active. Because PrefectDbtRunner runs node tasks in threads within the same process, the suppression will apply to those per-model tasks created during the compile call. If you have other, unrelated tasks running concurrently in the same process at the same time, they’ll be affected during the window too.
- If you also want to quiet dbt’s own event spam, set PrefectDbtRunner’s dbt log level (this does not affect the engine “Finished in state …” lines, which the options above handle):
from dbt_common.events.base_types import EventLevel
from prefect_dbt import PrefectDbtRunner, PrefectDbtSettings
PrefectDbtRunner(
settings=PrefectDbtSettings(log_level=EventLevel.WARN)
).invoke(["compile"])
If you tell me whether you want to keep ERROR logs for those dbt node tasks or hide everything, I can tailor one of the snippets above precisely to your setup.Preston Sharpe
03/09/2026, 3:48 PM2026-03-09T15:46:02.106457855Z Exception in thread dbt-callback-processor:
2026-03-09T15:46:02.106854599Z Traceback (most recent call last):
2026-03-09T15:46:02.106862698Z File "/usr/local/lib/python3.12/threading.py", line 1075, in _bootstrap_inner
2026-03-09T15:46:02.127945236Z self.run()
2026-03-09T15:46:02.128470306Z File "/usr/local/lib/python3.12/threading.py", line 1012, in run
2026-03-09T15:46:02.128595710Z self._target(*self._args, **self._kwargs)
2026-03-09T15:46:02.128600587Z File "/usr/local/lib/python3.12/site-packages/prefect_dbt/core/runner.py", line 518, in _callback_worker
2026-03-09T15:46:02.128995849Z callback_func(event_msg)
2026-03-09T15:46:02.129004627Z File "/usr/local/lib/python3.12/site-packages/prefect_dbt/core/runner.py", line 660, in _process_node_started_sync
2026-03-09T15:46:02.129006810Z manifest_node, prefect_config = self._get_manifest_node_and_config(node_id)
2026-03-09T15:46:02.129801256Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-03-09T15:46:02.129810344Z File "/usr/local/lib/python3.12/site-packages/prefect_dbt/core/runner.py", line 367, in _get_manifest_node_and_config
2026-03-09T15:46:02.130226824Z manifest_node = self.manifest.nodes.get(node_id)
2026-03-09T15:46:02.130233258Z ^^^^^^^^^^^^^
2026-03-09T15:46:02.130234925Z File "/usr/local/lib/python3.12/site-packages/prefect_dbt/core/runner.py", line 180, in manifest
2026-03-09T15:46:02.130236577Z self._set_manifest_from_project_dir()
2026-03-09T15:46:02.130237643Z File "/usr/local/lib/python3.12/site-packages/prefect_dbt/core/runner.py", line 214, in _set_manifest_from_project_dir
2026-03-09T15:46:02.130238828Z self._manifest = Manifest.from_dict(json.load(f)) # type: ignore[reportUnknownMemberType]
2026-03-09T15:46:02.130239786Z ^^^^^^^^^^^^
2026-03-09T15:46:02.130240667Z File "/usr/local/lib/python3.12/json/__init__.py", line 293, in load
2026-03-09T15:46:02.137608887Z return loads(fp.read(),
2026-03-09T15:46:02.138047571Z ^^^^^^^^^^^^^^^^
2026-03-09T15:46:02.138055031Z File "/usr/local/lib/python3.12/json/__init__.py", line 346, in loads
2026-03-09T15:46:02.138350063Z return _default_decoder.decode(s)
2026-03-09T15:46:02.138366742Z ^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-03-09T15:46:02.138369030Z File "/usr/local/lib/python3.12/json/decoder.py", line 338, in decode
2026-03-09T15:46:02.139097535Z obj, end = self.raw_decode(s, idx=_w(s, 0).end())
2026-03-09T15:46:02.139474823Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-03-09T15:46:02.139496599Z File "/usr/local/lib/python3.12/json/decoder.py", line 354, in raw_decode
2026-03-09T15:46:02.139499112Z obj, end = self.scan_once(s, idx)
2026-03-09T15:46:02.139500174Z ^^^^^^^^^^^^^^^^^^^^^^
2026-03-09T15:46:02.139501065Z json.decoder.JSONDecodeError: Expecting ',' delimiter: line 1 column 298984 (char 298983)Marvin
03/09/2026, 3:48 PMMarvin
03/09/2026, 3:49 PMimport logging
from contextlib import contextmanager
from prefect import flow, task
from prefect_dbt import PrefectDbtRunner
@contextmanager
def set_logger_level(name, level):
logger = logging.getLogger(name)
old = logger.level
logger.setLevel(level)
try:
yield
finally:
logger.setLevel(old)
@task
def dbt_compile_quiet():
with set_logger_level("prefect.engine", logging.ERROR):
PrefectDbtRunner().invoke(["compile"])
@flow
def run():
dbt_compile_quiet()
# ... other steps ...
- Filter just the “Finished in state …” records (keeps other engine logs):
import logging
from prefect import flow, task
from prefect_dbt import PrefectDbtRunner
class SuppressEngineFinishedFilter(logging.Filter):
def filter(self, record):
return not (record.name == "prefect.engine" and "Finished in state" in record.getMessage())
@task
def dbt_compile_quiet():
engine = logging.getLogger("prefect.engine")
f = SuppressEngineFinishedFilter()
engine.addFilter(f)
try:
PrefectDbtRunner().invoke(["compile"])
finally:
engine.removeFilter(f)
@flow
def run():
dbt_compile_quiet()
If you also want to avoid per-model tasks entirely during compile (which also eliminates those engine lines and avoids the manifest-read race):
- Run compile with callbacks disabled for that step:
from prefect import flow, task
from prefect_dbt import PrefectDbtRunner
@task
def dbt_compile_no_callbacks():
# No per-node tasks; no engine "Finished..." lines; avoids manifest read during compile
PrefectDbtRunner(_disable_callbacks=True).invoke(["compile"])
@flow
def run():
dbt_compile_no_callbacks()
# Then run normal dbt steps (with callbacks) that need the manifest
PrefectDbtRunner().invoke(["build"])
- Or, call the dbt CLI directly for compile, then use PrefectDbtRunner afterward:
import subprocess
from prefect import flow, task
from prefect_dbt import PrefectDbtRunner
@task
def cli_compile(project_dir=".", profiles_dir=None):
cmd = ["dbt", "compile", "--project-dir", project_dir]
if profiles_dir:
cmd += ["--profiles-dir", profiles_dir]
# --quiet reduces dbt stdout chatter
cmd += ["--quiet"]
subprocess.run(cmd, check=True)
@flow
def run():
cli_compile()
# Now the manifest is fully written; safe to parse/use in subsequent tasks
PrefectDbtRunner().invoke(["build"])
A couple follow-ups to help us confirm whether there’s a separate issue to file:
- Can you reproduce that JSONDecodeError when you run compile without disable_logger (i.e., with Option A or B, or with callbacks disabled)?
- Which versions are you on (Prefect, prefect-dbt, dbt-core, and adapter)? If it repros without logger suppression, I can dig into issues/discussions or open a bug with those details.
If you prefer one of these approaches, I can tailor it to your exact runner settings (profiles_dir, project_dir, partial parse settings, etc.).Preston Sharpe
03/09/2026, 4:11 PMdisable_logger. I'm running dbt-core and not using prefect cloud.
I am on older versions:
• prefect==3.4.*
• prefect-dbt==0.7.*
I'll do some work to upgrade these libraries. Thanks for the support!Marvin
03/09/2026, 4:11 PMMarvin
03/09/2026, 4:12 PMdisable_logger("prefect.engine"), I’d stick with either:
- Temporarily raising prefect.engine to ERROR during compile (keeps failures visible), or
- Adding a focused filter that drops only “Finished in state …” lines
- Both avoid touching the callback thread timing and should be stable on your versions.
Upgrading Prefect and prefect-dbt is a good move. Recent 3.x releases include improvements in logging and the dbt integration. After you upgrade, if you still want a tighter solution just for compile, we can:
- Use _disable_callbacks=True for the compile step only (no per-model tasks/logs), then run your downstream parsing/tasks as usual
- Or route compile via dbt CLI with --quiet, then use PrefectDbtRunner for build/test
If you hit anything during the upgrade or want a snippet tailored to your project_dir/profiles_dir, just ping me.Nate
03/09/2026, 5:53 PMPreston Sharpe
03/09/2026, 6:36 PMNate
03/09/2026, 6:36 PM