Skip to content

[1.12.3 Bug] statically_parse_unrendered_config stringifies list/dict config values, causing false positives in upgrade_manifest_json_dbt_version / state:modified #16133

Description

@TomWalsh11

Is this a new bug in dbt-core?

  • I believe this is a new bug in dbt-core
  • I have searched the existing issues, and I could not find an existing issue for this bug

Current Behavior

When loading a --state manifest whose metadata.dbt_version differs from the
running dbt's version, upgrade_manifest_json_dbt_version
(core/dbt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py) re-derives each
node's unrendered_config by re-parsing its raw SQL's {{ config(...) }} call via
statically_parse_unrendered_config (core/dbt/clients/jinja_static.py) — gated by
state_modified_compare_more_unrendered_values (default true since 1.12).

That parser's construct_static_kwarg_value is typed -> str and stringifies
list/dict values unconditionally. {{ config(cluster_by=['id']) }} ends up with
unrendered_config['cluster_by'] == "['id']" instead of the real list ['id'].

Since same_config/same_contents compare unrendered_config with plain ==, this
type mismatch makes state:modified falsely flag the model as changed — even though
same_body/checksum (the actual SQL) is identical.

Affects any model with a list/dict-valued config key declared inline (cluster_by,
partition_by, grants, labels, meta, unique_key as a list, etc.) whenever
state:modified compares manifests from two different dbt-core versions/builds.

Expected Behavior

The static parser should preserve the real type of list/dict config values (or at
least produce something that round-trips via ast.literal_eval), so the upgrade merge
doesn't introduce a type mismatch against a freshly-parsed, unchanged model.

Steps To Reproduce

  1. {{ config(cluster_by=['id']) }} in a model's SQL.
  2. dbt parse, keep the manifest (the "new" side).
  3. Copy it, edit metadata.dbt_version to a different version string (the "old" side).
  4. dbt ls --select state:modified --state <old-manifest-dir>

Expected: model not selected. Actual: selected — unrendered_config['cluster_by']
was rewritten from ['id'] to "['id']" by the upgrade step.

Relevant log output

$ dbt ls --select state:modified --state ./old-manifest/
model.my_project.my_model

(despite identical `raw_code`/checksum on both sides)

Environment

- OS: macOS
- Python: 3.13.0
- dbt: 1.12.3 (also present in 1.12.0)

Which database adapter are you using with dbt?

bigquery

Additional Context

Root-caused against a real ~450-model project: calling same_body, same_config,
same_persisted_description, same_fqn, same_database_representation,
same_contract, same_ref_representation directly (via dbt.contracts.state.PreviousState)
reproduced the flagged/not-flagged status of every model exactly (366/366).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugengine:v1Concerns the dbt Core v1 engine (Python).status:triageAwaiting initial triage / categorization.triagetype:bugA defect: Fusion behaves incorrectly versus expected/reference behavior.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions