[v2 Bug] `show --inline` with dbt functions (udfs) errors with `Function ... not found in project. Searched for ...`
- Dominant language
- Rust
- Stars
- 13.8k
- Forks
- 2.6k
- Avg merge
- 21h 31m
- Merged PRs (30d)
- 56
Description
### Is this a new bug in dbt v2.x compared to the latest version of dbt 1.x?
- [x] I believe this is a new bug in dbt v2.x
- [x] I have searched the existing issues and could not find a duplicate
### Current Behavior
As per title, `--show inline` with udfs (https://docs.getdbt.com/docs/build/udfs?version=2) don't work as expected in v2 - this causes the "highlight line to preview" functionality to fail in dbt Studio.
### Expected Behavior
No errors.
### Steps To Reproduce
```yaml
# dbt_project.yml
name: dbt_basic_5
profile: sf
version: "1.0.0"
models:
dbt_basic_5:
+materialized: table
# functions/is_pos_int.yml
functions:
- name: is_pos_int # required
config:
volatility: deterministic
arguments: # optional
- name: a_string # required if arguments is specified
data_type: string # required if arguments is specified
description: The string that I want to check if it's representing a positive integer (like "10")
default_value: "'1'" # optional, available in Snowflake and Postgres
returns: # required
data_type: integer # required
```
```sql
-- functions/is_pos_int.sql
REGEXP_INSTR(a_string, '^[0-9]+$')
-- models/foo.sql
with x as (select 1 c) select {{ function('is_pos_int') }}(c) as is_pos_int from x
```
Build
```sh
$ dbtf build && dbtf show -s foo
dbt-fusion 2.0.0-preview.209
Loading profiles.yml
Succeeded [ 0.55s] function dbt_jyeo.is_pos_int (function)
Succeeded [ 10.09s] model dbt_jyeo.foo (table)
============================ Execution Summary =============================
Finished 'build' successfully for target 'dev' [16.5s]
Processed: 1 model | 1 function
Summary: 2 total | 2 success
dbt-fusion 2.0.0-preview.209
Loading profiles.yml
Query show_model_dbt_basic_5_foo
┌────────────┐
│ IS_POS_INT │
╞════════════╡
│ 1 │
└────────────┘
1 rows.
Succeeded [ 0.79s] model dbt_jyeo.foo (table)
=========================================== Execution Summary ============================================
Finished 'show' successfully for target 'dev' [3.7s]
```
Do an inline show:
```sh
$ dbtf show --inline "with x as (select 1 c) select {{ function('is_pos_int') }}(c) as is_pos_int from x"
dbt-fusion 2.0.0-preview.209
Loading profiles.yml
========================================== Errors and Warnings ===========================================
[error] [InvalidConfig (dbt1005)]: Function 'is_pos_int' not found in project. Searched for '.is_pos_int'
--> inline_fd677450.sql:1:31
=========================================== Execution Summary ============================================
Finished 'show' with 1 error for target 'dev' [670ms]
```
Inline show with dbt-core/v1:
```sh
$ dbt show --inline "with x as (select 1 c) select {{ function('is_pos_int') }}(c) as is_pos_int from x"
21:02:58 Running with dbt=1.11.11
21:02:59 State adapter: dbt-state v2.37.1 is disabled
21:02:59 Registered adapter: snowflake=1.11.5
21:02:59 Unable to do partial parsing because an error occurred. Switching to full reparse.
21:03:00 Found 1 model, 1 sql operation, 536 macros
21:03:00
21:03:00 Concurrency: 10 threads (target='dev')
21:03:00
Previewing inline node:
| IS_POS_INT |
| ---------- |
| 1 |
```
### Relevant log output
```shell
```
### Environment
```markdown
- OS: macOS
- CPU: ARM
- dbt distribution and version: (`dbt --version`)
dbt-fusion 2.0.0-preview.209
```
### Which database adapter are you using?
snowflake
### Is this a discrepancy vs. dbt 1.x?
- [x] Yes — this works in dbt 1.x but not in dbt v2.x
### Additional Context
Fidget RCA:
This is an error-code asymmetry between ref() and function(), not a function()-specific parse bug.
Compile/Preview on a highlighted selection or unsaved buffer routes through the LSP inline path (WorkspaceMessage::Show → run_inline_show → render_sql), which renders the buffer text against the last-saved project index rather than re-parsing. When a dependency in the buffer isn't in that saved index, ref() and function() diverge:
ref() miss → lookup_ref returns DependencyNotFound (dbt1048)
function() miss → lookup_function returns InvalidConfig (dbt1005) — the exact code in this ticket
The graceful fallback is gated purely on the error code. downgraded_node_dependency_warning only downgrades DisabledDependency and DependencyNotFound to a warning (NodeNotFoundOrDisabled / dbt1087); every other code returns None and stays a hard error:
{code}
let has_disabled_or_missing_dependency = match error.code {
ErrorCode::DisabledDependency | ErrorCode::DependencyNotFound => true,
_ => return None,
};
{code}
So ref() (1048) is eligible for downgrade and falls back with a warning; function() (1005) is not eligible and hard-fails. That is the entire asymmetry.
Why the working combination works: saved + nothing-highlighted compiles the real saved node, whose function() dependency is already resolved into the index, so the lookup hits and never reaches the error branch. Command-bar dbt compile --select works for the same reason — it bypasses the inline buffer path entirely. Both match the workarounds already documented in this ticket. This is also why the behavior is adapter-agnostic (BigQuery + Snowflake) — the divergence is in dependency resolution, upstream of any adapter code.
Source references
Downgrade gate (node_resolver.rs#L39-L42): https://github.com/dbt-labs/fs/blob/7e9baadb2137aeea6a663c357fc38fbc6fba5b8d/fs/sa/crates/dbt-jinja-utils/src/node_resolver.rs#L39-L42
lookup_ref miss → dbt1048 (node_resolver.rs#L592-L597): https://github.com/dbt-labs/fs/blob/7e9baadb2137aeea6a663c357fc38fbc6fba5b8d/fs/sa/crates/dbt-jinja-utils/src/node_resolver.rs#L592-L597
lookup_function miss → dbt1005 (node_resolver.rs#L742-L749): https://github.com/dbt-labs/fs/blob/7e9baadb2137aeea6a663c357fc38fbc6fba5b8d/fs/sa/crates/dbt-jinja-utils/src/node_resolver.rs#L742-L749
Error codes InvalidConfig = 1005, DependencyNotFound = 1048 (codes.rs#L41-L85): https://github.com/dbt-labs/fs/blob/7e9baadb2137aeea6a663c357fc38fbc6fba5b8d/fs/sa/crates/dbt-error/src/codes.rs#L41-L85
Inline render resolves "ref(), source(), macros" — function() not mentioned (handler.rs#L42-L45): https://github.com/dbt-labs/fs/blob/7e9baadb2137aeea6a663c357fc38fbc6fba5b8d/crates/dbt-lsp/src/show/handler.rs#L42-L45
Likely fix direction (for Fusion engineering): make lookup_function's not-found case downgrade-eligible like ref()/source() — either give it a distinct not-found error code that gets added to the downgrade set, or stop reusing the generic InvalidConfig. Note a blanket downgrade of InvalidConfig (1005) would be wrong, since that code is also used for genuinely fatal cases such as ambiguous refs.
Contributor guide
Assessment
This issue has not been assessed yet.