Pod

Yes, this is on purpose. Pod is built for agents, so the default page is plain HTML.
Human? View the normal website.
Agent? You probably prefer /mcp/dbt-plan/issues.md or /mcp/dbt-plan/issues.json, or Pod over MCP.

Reported issues for dbt-plan

Pod holds 10 of 10 GitHub reports that passed its relevance review. This can include external user reports, maintainer-confirmed bugs, and concrete feature gaps. Treat them as evidence to inspect, not a count of distinct defects.

Back to dbt-plan.

Most discussed

dbt-plan run --format json prints a non-JSON line before the JSON

$ dbt-plan run --format json
Snapshot saved to .dbt-plan/base
{
  "summary": {
...

The first line is not JSON. _do_snapshot prints "Snapshot saved to ..." to stdout (cli.py:234) on the way to _do_check printing the report (cli.py:1198), so json.loads on run's stdout fails.

check --format json is clean — the difference is that run calls snapshot first.

This matters because docs/llms.txt:64-66 lists run and check side by side as the two commands an agent can…

Read the thread · 2026-09-06 · open · 1 comment

The MCP server and the Action cannot reach --target-dir, and the MCP has no way to make a baseline

dbt-plan check takes --target-dir. Neither wrapper passes it:

A project with target-path: build in dbt_project.yml, or DBT_TARGET_PATH set, gets No compiled SQL found — which the MCP turns into verdict: error and the Action turns into a failed step under set -eu. Both are loud rather than falsely safe, and both are avoidable:…

Read the thread · 2026-09-06 · open · 1 comment

Non-UTF-8 compiled SQL crashes with a traceback and exit 1, which the Action reports as destructive

diff.py handles non-UTF-8 compiled SQL on purpose:

except UnicodeDecodeError:
    # Non-UTF-8 file: treat as modified with no cached SQL.
    # Callers will see base_sql=None / current_sql=None and
    # produce REVIEW REQUIRED — consistent with the false-safe-ban rule.
    diffs.append(ModelDiff(name, "modified", base_path, current_path))
    continue

cli.py:990 responds to that None by reading the same file again, unguarded:

if diff.current_sql is not None:…

[Read the thread](https://github.com/PresentJay/dbt-plan/issues/173) · 2026-09-06 · open · 0 comments

### The workflow ci-setup generates fails on warnings and never reaches its own Gate step, and the committed example is a different file

`dbt-plan ci-setup` writes `_CI_WORKFLOW` (`cli.py`). Its last two steps:

```yaml
      - name: Check current
        run: |
          git checkout ${{ github.event.pull_request.head.sha }}
          dbt compile
          dbt-plan check --format github >> $GITHUB_STEP_SUMMARY

      - name: Gate
        run: dbt-plan check

GitHub runs a run: block as bash -e {0}. dbt-plan check exits 2 on any warning, so on a warning:

Read the thread · 2026-09-06 · open · 0 comments

The MCP server calls an acknowledged DROP COLUMN safe, and never passes on a stale target

#135 is one of three ways the MCP server's verdict disagrees with the report it was built from. The other two are not fixed by deriving the verdict from refusals, because in both of them refusals is empty.

Measured, dbt-core 1.11.7 + dbt-duckdb 1.10.1, an incremental with sync_all_columns dropping tax.

1. An acknowledged drop becomes safe with nothing in refusals

# .dbt-plan.yml
acknowledge_models:
  - fct
$ dbt-plan check
DESTRUCTIVE  fct (incremental,…

[Read the thread](https://github.com/PresentJay/dbt-plan/issues/162) · 2026-09-06 · open · 0 comments

### A partial or empty baseline reports the whole project SAFE, because every model looks newly added

A baseline missing compiled SQL is not detected. Every model it lacks is treated as newly
added, and an added model is `SAFE` by rule (`predictor.py:237-238`).

Measured on dbt-core 1.11.7 + dbt-duckdb 1.10.1. `src` drops `tax`; `reader` selects it:

baseline: .dbt-plan/base/{manifest.json, compiled/models/src.sql, compiled/models/reader.sql}

$ dbt-plan check # intact DESTRUCTIVE src (view)

BROKEN_REF reader: reads dropped column(s): tax exit=1

$ rm…

Read the thread · 2026-09-06 · open · 0 comments

A unit test whose given input is a versioned model is never checked, because two names for one model never meet

#43 taught cascade to check unit-test fixtures against dropped columns. It misses whenever the fixture's given input is a versioned model.

Measured on dbt-core 1.11.7 + dbt-duckdb 1.10.1:

models:
  - name: stg_orders
    latest_version: 2
    versions: [{v: 2}]
unit_tests:
  - name: t_fct
    model: fct
    given:
      - input: ref('stg_orders', v=2)
        rows: [{id: 1, amount: 2, tax: 3}]

stg_orders_v2 drops tax. The fixture still supplies it.

$ dbt-plan check…

[Read the thread](https://github.com/PresentJay/dbt-plan/issues/157) · 2026-09-06 · open · 0 comments

### A corrupt or missing base manifest silently turns off deleted-model detection, and exit 0 comes back

`check` reads the base manifest best-effort. When it cannot be read, `base_node_index`
becomes `{}` and everything that depends on it turns off — including the deleted-model
detection #129 added — with nothing printed, not even under `-v`.

Measured on dbt-core 1.11.7 + dbt-duckdb 1.10.1. A model deleted from the project, its
orphan still in `target/compiled/` as #127 describes:

$ dbt-plan check # base manifest intact DESTRUCTIVE doomed (view) MODEL REMOVED…

Read the thread · 2026-09-06 · open · 0 comments

Most recent

One unnamed projection discards every column name in the model, and since #131 that means exit 2

extract_columns returns None for the whole model when any single projection has no name (columns.py:225-228). Measured:

aliased everything         -> ['id', 'total']
unaliased count(*)         -> None      <-- whole model unreadable
unaliased coalesce         -> None
unaliased case             -> None
unaliased arithmetic       -> None
unaliased lower()          -> None
unaliased window fn        -> None
unaliased column ref       -> ['id', 'amount']
qualified unaliased col    ->…

[Read the thread](https://github.com/PresentJay/dbt-plan/issues/154) · 2026-09-06 · open · 0 comments

### The MCP server answers safe when warning_exit_code is 0, including for refusals

The MCP server's module docstring sets the rule it is built around:

> If this ever answers `{"safe": true}` for something dbt-plan actually declined to
> judge, it is worse than not existing.

It can, with one line of config.

`server.py:47` maps the exit code straight to a verdict:

```python
_VERDICTS = {0: "safe", 1: "destructive", 2: "review_required"}

.dbt-plan.yml can set what that 2 is:

warning_exit_code: 0

_exit_code_for (cli.py:455) then returns 0 for every…

Read the thread · 2026-09-05 · open · 0 comments

The remaining reports are on the project's issue tracker.