Uh oh!
There was an error while loading. Please reload this page.
Suggest "did you mean ...?" for unknown key lookups and variable references - #6208
Conversation
Integration test reportCommit: e6c54ea
8 interesting tests: 4 SKIP, 3 KNOWN, 1 RECOVERED
Top 3 slowest tests (at least 2 minutes):
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| @@ -0,0 +1,13 @@ | |||
| >>> errcode [CLI] bundle validate | |||
| Error: reference does not exist: ${var.hst}, did you mean "host"? | |||
There was a problem hiding this comment.
seems ambiguous, maybe the suggestion can be more of a drop-in replacement?
| Error: reference does not exist: ${var.hst}, did you mean "host"? | |
| Error: reference does not exist: ${var.hst}, did you mean ${var.host}? |
There was a problem hiding this comment.
This would get confusing where there are more than one suggestions, especially for long variable names with multiple layers of nesting
There was a problem hiding this comment.
We could write something like
Error: reference does not exist: ${var.hst}, did you mean "host" in place of "hst"
There was a problem hiding this comment.
+1 for having the full replacement. Those error messsage more explicit and easier to copy paste.
This would get confusing where there are more than one suggestions, especially for long variable names with multiple layers of nesting
Could you elaborate or give an example?
There was a problem hiding this comment.
especially for long variable names with multiple layers of nesting
This is interesting - can you please add an acceptance test for multiple layers to capture behaviour? E.g. how do you resolve
resources:
jobs:
nightly:
${resources.jobbs.nightlyy}
There was a problem hiding this comment.
This is interesting - can you please add an acceptance test for multiple layers to capture behaviour? E.g. how do you resolve
This would just reply with the first error since as soon as we find an error we stop resolving variable names. Added the test double-nested-typo
There was a problem hiding this comment.
+1 for having the full replacement. Those error messsage more explicit and easier to copy paste.
This would get confusing where there are more than one suggestions, especially for long variable names with multiple layers of nesting
Could you elaborate or give an example?
Actually after thinking some more about this I agree. I'll add the recommendations in seperate lines instead of in one line should make it much cleaner
There was a problem hiding this comment.
you can use libs/diag for the errors, it allows you to pass extra context
There was a problem hiding this comment.
I didn't need to use libs/diag it worked without it as well
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| if len(suggestions) > 1 { | ||
| header = "did you mean one of:" | ||
| } | ||
| return "\n\n" + header + "\n" + strings.Join(lines, "\n") |
There was a problem hiding this comment.
this is fragile and should not be done. libs/diag handles proper formatting of multi-line errors in various terminal environments & correct interleaving of messages.
since you can't import libs/diag inside libs/dyn, I suggest you propagate the suggestion upwards and handle it there
Co-authored-by: Isaac
Co-authored-by: Jan N Rose <janniklas.rose@gmail.com>
Co-authored-by: Isaac
Cover deeply-nested references, two typos in one reference (outermost key wins), and multiple suggestions for the nested cases. Co-authored-by: Isaac
b964d6c to
7d038a4ComparePropagate reference suggestions as structured data (dynvar.ReferenceError + dyn.SuggestedReferences) instead of formatting a multi-line error string in libs/dyn. The mutator builds the diagnostic Detail so libs/diag owns terminal formatting. Rendered output is unchanged. Co-authored-by: Isaac
Co-authored-by: Isaac
| Error: reference does not exist: ${var.hst} | ||
| did you mean: | ||
| ${var.host} |
There was a problem hiding this comment.
nice, this is now very clear and actionable!
| Error: reference does not exist: ${var.clustr.spark_versio} | ||
| did you mean: | ||
| ${var.cluster.spark_versio} |
There was a problem hiding this comment.
this isn't great, but not much we can do since we fail fast and traversing the suggestion in search for valid references is probably overkill
Uh oh!
There was an error while loading. Please reload this page.
## Release v1.13.0 ### Notable Changes * `bundle deploy` now reports the per-resource actions it took, how many files it synced, and a summary of created/changed/deleted/unchanged resources; `bundle destroy` reports how many resources it deleted. `-q` prints only the summaries, `-qq` only warnings and errors. ([#5720](#5720)) ### CLI * `databricks aitools install` now supports Goose, installing Databricks agent skills into its skills directory. * Error messages for failed key lookups and variable references now suggest the closest matching key if one is found. ([#6208](#6208)) * Released binaries are now built against the FIPS 140-3 validated Go Cryptographic Module, with FIPS 140-3 mode enabled by default. TLS connections negotiate only FIPS-approved cipher suites, which drops ChaCha20 and CBC from what the client offers. FIPS mode can be disabled at startup with `GODEBUG=fips140=off`, which restores the previous TLS behaviour ([#6262](#6262)). * `databricks environments setup-local` now removes a `databricks-connect` pin from `[project].dependencies`, an optional-dependency extra, or a dependency group when its version range conflicts with the compute target's `databricks-connect` version, so `uv sync` no longer fails with an unsatisfiable resolution when a template ships a conflicting pin. A pin that co-resolves, carries no version, or is marker-gated is left untouched, and each removed pin is reported with the new `W_DBCONNECT_CONSOLIDATED` warning. Wildcard version pins such as `==15.1.*` are now also checked for conflicts with the environment's constraints. ### Bundles * Allow dashes in the catalog and schema names prompted by `databricks bundle init`, and backtick-quote the catalog and schema identifiers in the SQL generated by the built-in templates so names with dashes work at runtime. * Fixed `bundle.git.branch`, `bundle.git.commit`, and `bundle.git.origin_url` being empty for bundles deployed from a workspace Git folder that has Git CLI access. The workspace API does not report git metadata for those folders, so it is now read from the Repos API instead. * direct: job_runs deploy progress lines now include the resource key (e.g. `Output from job_runs.foo: id=123: ...`) so concurrent runs are easier to tell apart. * direct: `resources.job_runs` can set `lifecycle.triggers.on_bundle_deploy: true` to re-fire the run on every bundle deploy. Removing the trigger does not recreate the existing run. * When migrating a bundle to the direct deployment engine, resources that only the direct engine supports (e.g. instance pools, catalogs) are now skipped by the deploy that migrates the state instead of failing it. They are created by the next deploy, which runs on the migrated state. * Warn on invalid `secret_scopes` permission levels (`READ`, `WRITE`, `MANAGE`); fail under `bundle validate --strict`. * Reject secret scope permissions that name no principal, instead of failing after the scope is created. * Write the deployment state atomically so an interrupted save cannot leave a state file that the CLI refuses to read. * Warn when the deployment state was last written by a newer CLI version than the one running. * Support pip extras (e.g. `[train]`) on local wheels in a job environment's `dependencies` ([#1602](#1602)). ### Dependency Updates * Bump `github.com/databricks/databricks-sdk-go` from v0.170.0 to v0.171.0 ([#6320](#6320)). * Bump the Databricks Terraform provider to 1.127.0 ([#6319](#6319)). * Bump Go toolchain to 1.26.6 ([#6266](#6266)). * Bump Go toolchain to 1.26.7 ([#6325](#6325)).
Changes
When a
${...}variable reference fails to resolve, the error now lists the closest matching keys (edit distance ≤ 2) as full drop-in replacement references — each on its own line, with only the failed path segment swapped:Multiple candidates use
did you mean one of::Only the failed segment is corrected; the prefix and the rest of the path are preserved, so nested and indexed references reconstruct correctly:
${var.clustr.spark_version}→${var.cluster.spark_version}(outer key)${var.cluster.spark_versio}→${var.cluster.spark_version}(leaf)${var.librariez[0].jar}→${var.libraries[0].jar}(index preserved)Implementation
The suggestion logic lives in
libs/dyn/suggest.go:levenshteinDistance— edit distance between two strings.suggestKeys— keys of aMappingwithinmaxSuggestionDistance(2) of the missing key, sorted by increasing distance (ties keep insertion order).didYouMean— single-line clause (, did you mean "x"?) used by the generic key-lookup error.didYouMeanReferences/replaceKey— build the multi-line drop-in block, rebuilding each reference from the original text viaPath.String()(so index components survive).noSuchKeyErrorcarries the suggestions, computed inpathComponent.visitwhere the parent map is in scope. Variable interpolation inlibs/dyn/dynvarrewrites the not-found message and discards the original error, so it re-attaches the drop-in block via the exporteddyn.DidYouMeanReferences(err, reference), passing the original (pre-rewrite) reference text.Scope
Because the underlying key lookup is generic, suggestions are drawn from the siblings at the level where the lookup fails, so this covers more than
${var.*}:${var.my_catlog}→ other variable names${workspace.rooot_path}/${bundle.naem}→ sibling config fieldsBehavior notes:
bundle,workspace, andvariablesprefixes are resolved at validate time;${resources.*}references are left unresolved until deploy, so a typo there produces no suggestion.${var.clustr.spark_versio}→${var.cluster.spark_versio}).${wrkspace.host}) is skipped during resolution and never reaches a key lookup, so no suggestion is produced.dyn.GetByPathkey-not-found error keeps a single-line bare-key hint (key not found at "baz", did you mean "bar"?), since there is no${...}reference to rebuild there.Why
A mistyped variable reference in
databricks.yml(e.g.${var.my_catlog}instead of${var.my_catalog}) previously failed with a barereference does not exist: ${var.my_catlog}and no hint, even though the CLI knows every valid key at the point of failure. The CLI already offers this for mistyped command-line flags; this brings the same guidance to variable interpolation, and — because the hint is attached at the genericdynlayer — to every not-found key lookup. Rendering suggestions as full drop-in references makes them explicit and copy-pasteable.Suggestions are shown only when a candidate is within edit distance 2; otherwise the error is unchanged, so unrelated typos don't produce noisy or misleading hints.
Tests
libs/dyn/suggest_test.go—levenshteinDistance,suggestKeys(distance threshold, ordering, empty map),didYouMean, andDidYouMeanReferences(single, multiple, nested outer/leaf typo, index component, non-var prefix, no suggestions, fallback).libs/dyn/visit_get_test.go— a close key produces a suggestion; a far-off key produces none.libs/dyn/dynvar/resolve_test.go—TestResolveNotFoundSuggestsCloseKeyasserts the hint flows through variable interpolation.acceptance/bundle/variables/reference-typo/— six scenarios capturing exact CLI output: single suggestion, multiple suggestions, non-var (workspace) multiple, multiple typos where only the first is reported, two typos in one reference (outermost wins), and a double-nested typo with multiple suggestions.This pull request and its description were written by Isaac.