Skip to content

Conversation

@kiliantscherny
Copy link

@kiliantscherny kiliantscherny commented Dec 19, 2025

Summary

This PR improves dbt command handling in Tower CLI with better handling of commands that accept a --select argument, and also handles the different result types of those commands (iterable vs non-iterable), to prevent the app crashing.

Current issues

1. Commands that don't support --select have it added to them

Imagine I want to build a model called stg_economic_booked_invoices. In a dbt job, dbt deps will be run to install package dependencies, before proceeding with dbt build

App run result
2025-12-19 14:34:00 | INFO | Starting dbt workflow | project=/tmp/.tmpIuEF6X/app commands=['deps', 'build'] target=<default> selector=stg_economic_booked_invoices full_refresh=False
2025-12-19 14:34:00 | INFO | Running dbt deps --select stg_economic_booked_invoices ...
2025-12-19 14:34:00 | ERROR | dbt command failed: deps --select stg_economic_booked_invoices | detail=No such option: --select
Traceback (most recent call last):
  File "/tmp/.tmpIuEF6X/app/./task.py", line 282, in <module>
    main()
    ~~~~^^
  File "/tmp/.tmpIuEF6X/app/./task.py", line 268, in main
    run_dbt()
    ~~~~~~~^^
  File "/tmp/.tmpIuEF6X/app/./task.py", line 106, in run_dbt
    workflow.run()
    ~~~~~~~~~~~~^^
  File "/tmp/.tmpIuEF6X/app/.venv/lib/python3.13/site-packages/tower/_dbt.py", line 140, in run
    return run_dbt_workflow(self.config)
  File "/tmp/.tmpIuEF6X/app/.venv/lib/python3.13/site-packages/tower/_dbt.py", line 213, in run_dbt_workflow
    raise RuntimeError(f"dbt command failed: {' '.join(args)}")
RuntimeError: dbt command failed: deps --select stg_economic_booked_invoices

What is happening above is that when I chose the DBT_SELECT, it passes this to dbt commands that don't support this, like dbt deps, causing a crash.

2. Non-iterable result objects attempting to be iterated over

When I add docs generate as one of the DBT_COMMANDS env vars, it will fail the app run due to the result of dbt docs generate not being an iterable object (unlike the result of dbt build). I get:

TypeError: 'CatalogArtifact' object is not iterable
App run result
2025-12-19 14:42:40 | 2025-12-19 15:42:40 | INFO | Running dbt docs generate --select stg_economic_booked_invoices ...
2025-12-19 14:42:40 | 14:42:40  Running with dbt=1.10.5
2025-12-19 14:42:40 | 14:42:40  Registered adapter: bigquery=1.10.0
[...]
2025-12-19 14:42:43 | 14:42:43  Catalog written to /private/var/folders/yz/_kt3ml8n611b9wm8wpbb9vx40000gn/T/P8RQRTGWw2-tower-package/app/target/catalog.json
2025-12-19 14:42:43 | 2025-12-19 15:42:43 | DEBUG | Starting new HTTPS connection (1): fishtownanalytics.sinter-collect.com:443
2025-12-19 14:42:43 | 2025-12-19 15:42:43 | DEBUG | https://fishtownanalytics.sinter-collect.com:443 "POST /com.snowplowanalytics.snowplow/tp2 HTTP/1.1" 200 2
2025-12-19 14:42:43 | Traceback (most recent call last):
2025-12-19 14:42:43 |   File "/var/folders/yz/_kt3ml8n611b9wm8wpbb9vx40000gn/T/P8RQRTGWw2-tower-package/app/./task.py", line 282, in <module>
2025-12-19 14:42:43 |     main()
2025-12-19 14:42:43 |     ~~~~^^
2025-12-19 14:42:43 |   File "/var/folders/yz/_kt3ml8n611b9wm8wpbb9vx40000gn/T/P8RQRTGWw2-tower-package/app/./task.py", line 268, in main
2025-12-19 14:42:43 |     run_dbt()
2025-12-19 14:42:43 |     ~~~~~~~^^
2025-12-19 14:42:43 |   File "/var/folders/yz/_kt3ml8n611b9wm8wpbb9vx40000gn/T/P8RQRTGWw2-tower-package/app/./task.py", line 106, in run_dbt
2025-12-19 14:42:43 |     workflow.run()
2025-12-19 14:42:43 |     ~~~~~~~~~~~~^^
2025-12-19 14:42:43 |   File "/private/var/folders/yz/_kt3ml8n611b9wm8wpbb9vx40000gn/T/P8RQRTGWw2-tower-package/app/.venv/lib/python3.13/site-packages/tower/_dbt.py", line 158, in run
2025-12-19 14:42:43 |     return run_dbt_workflow(self.config)
2025-12-19 14:42:43 |   File "/private/var/folders/yz/_kt3ml8n611b9wm8wpbb9vx40000gn/T/P8RQRTGWw2-tower-package/app/.venv/lib/python3.13/site-packages/tower/_dbt.py", line 224, in run_dbt_workflow
2025-12-19 14:42:43 |     _log_run_results(config.logger, getattr(result, "result", None))
2025-12-19 14:42:43 |     ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-12-19 14:42:43 |   File "/private/var/folders/yz/_kt3ml8n611b9wm8wpbb9vx40000gn/T/P8RQRTGWw2-tower-package/app/.venv/lib/python3.13/site-packages/tower/_dbt.py", line 263, in _log_run_results
2025-12-19 14:42:43 |     for entry in entries:
2025-12-19 14:42:43 |                  ^^^^^^^
2025-12-19 14:42:43 | TypeError: 'CatalogArtifact' object is not iterable
Oh no! Your local run crashed!
Error: App crashed during local execution

Changes

1. Selector flag improvements:

  • Added COMMANDS_WITH_SELECT frozenset to explicitly define which dbt commands support the --select flag (run, test, build, compile, seed, snapshot, docs, list/ls, show, source) – these can be found here: https://docs.getdbt.com/reference/node-selection/syntax
  • Updated logic in run_dbt_workflow to only add --select flag to commands that support it, preventing errors with commands like docs generate, clean, deps, etc.

2. Result logging improvements:

  • Enhanced _log_run_results function to handle different dbt command return types correctly
  • Added type checking to distinguish between:
    • Commands returning iterable RunExecutionResult (run, test, build, etc.)
    • Commands returning non-iterable results (CatalogArtifact, Manifest, bool, None)
  • Added debug logging when non-iterable results are encountered
  • Prevents crashes when trying to iterate over results from commands like docs generate or debug

3. Comprehensive test coverage:

  • Added parametrized tests covering all 11 commands in COMMANDS_WITH_SELECT to ensure they properly receive the --select flag when a selector is provided
  • Added parametrized tests for commands that don't support --select (deps, clean, debug, init) to verify the flag is not added
  • Added tests for edge cases:
    • Preventing duplicate --select flags when already present in command args
    • Mixed command workflows with both supported and unsupported commands
  • Total test coverage increased from 9 to 24 tests for the TestRunDbtWorkflow class

Impact

These changes fix potential crashes when running certain dbt commands – particularly dbt deps – that don't support --select, as well as crashes from those commands that return non-iterable results.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes crashes in dbt command handling by addressing two issues: commands incorrectly receiving the --select flag when they don't support it (e.g., dbt deps), and the app attempting to iterate over non-iterable result objects from commands like dbt docs generate.

  • Added COMMANDS_WITH_SELECT frozenset to explicitly control which commands receive the --select flag
  • Enhanced _log_run_results to handle both iterable (RunExecutionResult) and non-iterable result types (CatalogArtifact, Manifest, bool, None)
  • Added type checking and debug logging for non-iterable results to prevent crashes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@kiliantscherny kiliantscherny changed the base branch from main to develop December 19, 2025 16:04
Copy link
Contributor

@sammuti sammuti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM other than the missing test coverage as suggested.

Thanks for the patch!

@sammuti sammuti merged commit 6239423 into tower:develop Dec 23, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants