Validation Failing with`experimental.immutable_folder: true` Despite Plan and Deploy Succeeding
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
調査の方向性
Start at bundle/config/mutator/override_immutable_folder.go and bundle/config/validate/folder_permissions.go, then find where the validate pipeline is assembled versus the plan/deploy pipeline (which runs the snapshot.Upload mutator that creates internal_immutable_snapshots.immutable). Determine why the resource reference stays a literal ${...} during validate. Done: databricks bundle validate no longer sends the unresolved snapshot path to GET /api/2.0/workspace/get-status, covered by a test around the validate mutator.
索引モデルが issue の本文から書いたものです。
説明
Describe the issue
bundle validate fails when experimental.immutable_folder: true is set, even though bundle plan and bundle deploy seem to work correctly.
The OverrideImmutableFolder mutator rewrites workspace.artifact_path and workspace.file_path to reference ${resources.internal_immutable_snapshots.immutable.full_path}. During plan and deploy, the internal_immutable_snapshots.immutable resource is created and the reference resolves correctly. During validate, however, the resource is never created - the reference passes through as a literal string, and the validate:folder_permissions mutator sends it to the workspace API, which rejects it.
Configuration
Minimal databricks.yml to reproduce:
bundle:
name: my.bundle
engine: direct
experimental:
immutable_folder: true
workspace:
root_path: /Workspace/Bundles/my/bundle
targets:
dev:
workspace:
host: https://<workspace-url>
run_as:
service_principal_name: <service-principal-id>
permissions:
- level: CAN_MANAGE
service_principal_name: <service-principal-id>
There are also a few basic example jobs and SDP pipelines that point to notebooks using relative paths e.g. ../notebooks/hello_world.ipynb.
Note: I was testing using a pre-existing bundle that was already deployed using the direct engine prior to enabling immutable_folder. I did not test with a brand-new (never-deployed-before) bundle. Tested once with a custom workspace.root_path and once with the default and got the same result both times.
Steps to reproduce the behavior
- Set
experimental.immutable_folder: trueindatabricks.yml - Run
databricks bundle validate --target dev(as a service principal) - See error:
Error: Path (${resources.internal_immutable_snapshots.immutable.full_path}/artifacts) doesn't start with '/' Error: Path (${resources.internal_immutable_snapshots.immutable.full_path}/files) doesn't start with '/'
Note: bundle plan and bundle deploy both succeed — the internal_immutable_snapshots.immutable resource is created and the snapshot path resolves correctly. Only validate is affected.
Expected Behavior
Unsure. I assume bundle validate should either resolve the internal_immutable_snapshots.immutable resource (as plan and deploy do), or the validate:folder_permissions step should skip validation for paths that reference immutable snapshot resources that are only created at deploy time.
Actual Behavior
The mutator runs and successfully rewrites:
workspace.artifact_path→${resources.internal_immutable_snapshots.immutable.full_path}/artifactsworkspace.file_path→${resources.internal_immutable_snapshots.immutable.full_path}/files
However, during validate, the internal_immutable_snapshots.immutable resource is never created (it is absent from bundle validate --output json). The variable reference passes through as the literal string ${resources.internal_immutable_snapshots.immutable.full_path}, and validate:folder_permissions sends it to the GET /api/2.0/workspace/get-status endpoint, which rejects it because it doesn't start with /.
In contrast, plan and deploy both create the resource successfully:
planoutput showsrecreate internal_immutable_snapshots.immutableand thesnapshot.Uploadmutator resolves the path to/Workspace/Users/<sp-id>/.snapshots/<bundle-hash>/<deployment-hash>deployoutput showsRecreated internal_immutable_snapshots.immutableand creates the snapshot viaPOST /api/2.0/repos/snapshots, with job notebook paths correctly pointing to the snapshot folder
OS and CLI version
- CLI version: v1.19.0 (latest)
- OS: macOS 26.6
Is this a regression?
Unknown - this is my first time testing experimental.immutable_folder. I have not tested with any prior CLI version.
Detailed plan
bundle plan output:
Building my_bundle_custom_lib_a...
recreate internal_immutable_snapshots.immutable
update jobs.example_job
update jobs.sp_rights_analysis
update pipelines.example_pipeline
Plan: 1 to add, 3 to change, 1 to delete, 10 unchanged
Filtered debug output of bundle plan --log-level DEBUG -t dev 2>&1 | grep -i "immutable\|snapshot":
18:07:42 Debug: Apply pid=81169 mutator=OverrideImmutableFolder
18:07:49 Debug: Apply pid=81169 mutator=snapshot.Upload
18:07:49 Debug: GET /api/2.0/repos/snapshots/rootpath
< "path": "/Workspace/Users/<sp-id>/.snapshots/"
< } pid=81169 mutator=snapshot.Upload sdk=true
18:07:49 Debug: GET /api/2.0/repos/snapshots/rootpath
< "path": "/Workspace/Users/<sp-id>/.snapshots/"
18:07:49 Debug: GET /api/2.0/workspace/get-status?path=/Workspace/Users/<sp-id>/.snapshots/<bundle-hash>/<deployment-hash>
< "path": "/Workspace/Users/<sp-id>/.snapshots/<bundle-hash>/<deployment-hash>",
recreate internal_immutable_snapshots.immutable
Redacted bundle plan -o json (trimmed to the immutable snapshot resource and one job showing the dependency – other resources omitted for brevity):
{
"plan_version": 2,
"cli_version": "1.19.0",
"plan": {
"resources.internal_immutable_snapshots.immutable": {
"action": "recreate",
"new_state": {
"value": {
"relative_path": "<bundle-hash>/<new-deployment-hash>",
"full_path": "/Workspace/Users/<deployer-sp-id>/.snapshots/<bundle-hash>/<new-deployment-hash>",
"bundle_id": "<bundle-hash>",
"acl": [
{ "user_name": "<run-as-sp-id>", "permission_level": "CAN_READ" },
{ "service_principal_name": "<run-as-sp-id>", "permission_level": "CAN_READ" },
{ "user_name": "<user-email>", "permission_level": "CAN_READ" }
],
"zip_path": "<local-path>/.databricks/bundle/dev/snapshots/<new-deployment-hash>.zip"
}
},
"remote_state": {
"relative_path": "<bundle-hash>/<old-deployment-hash>",
"full_path": "/Workspace/Users/<deployer-sp-id>/.snapshots/<bundle-hash>/<old-deployment-hash>"
},
"changes": {
"full_path": {
"action": "recreate",
"reason": "immutable",
"old": "/Workspace/Users/<deployer-sp-id>/.snapshots/<bundle-hash>/<old-deployment-hash>",
"new": "/Workspace/Users/<deployer-sp-id>/.snapshots/<bundle-hash>/<new-deployment-hash>",
"remote": "/Workspace/Users/<deployer-sp-id>/.snapshots/<bundle-hash>/<old-deployment-hash>"
},
"relative_path": {
"action": "recreate",
"reason": "immutable"
},
"zip_path": {
"action": "recreate",
"reason": "immutable"
}
}
},
"resources.jobs.example_job": {
"depends_on": [
{
"node": "resources.internal_immutable_snapshots.immutable",
"label": "${resources.internal_immutable_snapshots.immutable.full_path}"
}
],
"action": "update",
"new_state": {
"value": {
"tasks": [
{
"notebook_task": {
"notebook_path": "/Workspace/Users/<deployer-sp-id>/.snapshots/<bundle-hash>/<new-deployment-hash>/files/src/main/databricks/notebooks/hello_world"
},
"task_key": "serverless_task"
}
]
}
},
"changes": {
"tasks[task_key='serverless_task'].notebook_task.notebook_path": {
"action": "update",
"old": "/Workspace/Users/<deployer-sp-id>/.snapshots/<bundle-hash>/<old-deployment-hash>/files/src/main/databricks/notebooks/hello_world",
"new": "/Workspace/Users/<deployer-sp-id>/.snapshots/<bundle-hash>/<new-deployment-hash>/files/src/main/databricks/notebooks/hello_world",
"remote": "/Workspace/Users/<deployer-sp-id>/.snapshots/<bundle-hash>/<old-deployment-hash>/files/src/main/databricks/notebooks/hello_world"
}
}
}
}
}
Key observations from the plan:
plancorrectly creates theinternal_immutable_snapshots.immutableresource withaction: "recreate"andreason: "immutable", resolvingfull_pathto a real snapshot path- Jobs depend on
resources.internal_immutable_snapshots.immutableand theirnotebook_pathvalues update to the new snapshot hash planruns thesnapshot.Uploadmutator (which callsGET /api/2.0/repos/snapshots/rootpath), butvalidatedoes not – it only runsOverrideImmutableFolder
Debug Logs
bundle validate (fails)
databricks bundle validate -t dev
Error: Path (${resources.internal_immutable_snapshots.immutable.full_path}/artifacts) doesn't start with '/' (400 INVALID_PARAMETER_VALUE)
Endpoint: GET https://<workspace-url>/api/2.0/workspace/get-status?path=%24%7Bresources.internal_immutable_snapshots.immutable.full_path%7D%2Fartifacts
HTTP Status: 400 Bad Request
API error_code: INVALID_PARAMETER_VALUE
API message: Path (${resources.internal_immutable_snapshots.immutable.full_path}/artifacts) doesn't start with '/'
Error: Path (${resources.internal_immutable_snapshots.immutable.full_path}/files) doesn't start with '/' (400 INVALID_PARAMETER_VALUE)
Endpoint: GET https://<workspace-url>/api/2.0/workspace/get-status?path=%24%7Bresources.internal_immutable_snapshots.immutable.full_path%7D%2Ffiles
HTTP Status: 400 Bad Request
API error_code: INVALID_PARAMETER_VALUE
API message: Path (${resources.internal_immutable_snapshots.immutable.full_path}/files) doesn't start with '/'
Name: my.bundle
Target: dev
Workspace:
Host: https://<workspace-url>
User: <sp-id>
Path: /Workspace/Bundles/my/bundle
Found 2 errors
Filtered debug output (bundle validate --log-level DEBUG -t dev 2>&1 | grep -i "immutable\|snapshot"):
18:06:23 Debug: Apply pid=80390 mutator=OverrideImmutableFolder
18:06:24 Debug: GET /api/2.0/workspace/get-status?path=${resources.internal_immutable_snapshots.immutable.full_path}/files
< "message": "Path (${resources.internal_immutable_snapshots.immutable.full_path}/files) doesn't start with '/... (1 more bytes)"
18:06:24 Debug: non-retriable error: Path (${resources.internal_immutable_snapshots.immutable.full_path}/files) doesn't start with '/' pid=80390 mutator=validate:folder_permissions sdk=true
18:06:24 Debug: GET /api/2.0/workspace/get-status?path=${resources.internal_immutable_snapshots.immutable.full_path}/artifacts
< "message": "Path (${resources.internal_immutable_snapshots.immutable.full_path}/artifacts) doesn't start wit... (5 more bytes)"
18:06:24 Debug: non-retriable error: Path (${resources.internal_immutable_snapshots.immutable.full_path}/artifacts) doesn't start with '/' pid=80390 mutator=validate:folder_permissions sdk=true
bundle deploy (succeeds)
databricks bundle deploy -t dev
Building my_bundle_custom_lib_a...
Recreated internal_immutable_snapshots.immutable
Updated jobs.sp_rights_analysis
Updated jobs.example_job
Updated pipelines.example_pipeline
Files: 0 uploaded, 0 deleted
Resources: 1 created, 3 changed, 1 deleted, 10 unchanged
Filtered debug output (bundle deploy --log-level DEBUG -t dev 2>&1 | grep -i "immutable\|snapshot") confirms the snapshot.Upload mutator runs and the resource is created via POST /api/2.0/repos/snapshots, with notebook paths correctly resolving to /Workspace/Users/<sp-id>/.snapshots/<bundle-hash>/<snapshot-hash>/files/....
- 主要言語
- Go
- スター
- 404
- フォーク
- 246
- 平均マージ
- 1日 14時間
- マージ済み PR(30日)
- 295
環境構築
- Dockerfile または Docker Compose ファイルあり
- プルリクエストのテンプレートあり
- コントリビューションガイドなし
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
databricks/cli のほかの issue
-
Apps commands only accept relative path to app.yml対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープンCLI
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
databricks/cli#6910 ·
メンテナーはふだん 1 日以内に返信
-
DABs
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
databricks/cli#6670 ·
メンテナーはふだん 1 日以内に返信
-
DABs
難易度 3/5 1〜2日 初心者へのやさしさ 68/100
databricks/cli#6901 ·
メンテナーはふだん 1 日以内に返信
-
Feature request: allow bundle init --config-file to read from Unity Catalog Volumes対応中かも @leprpht が 10 日前に担当しました。 オープン
難易度 4/5 3〜5日 初心者へのやさしさ 68/100
databricks/cli#6786 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
databricks/cli#6785 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
似ている issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
vanderheijden86/b9s#20 ·
-
go-battery needs an ndsctl on PATH: TestPurchaseSessionGuardHoldsThroughTheOutcomeUnknownWindow fails on bare hosts (passes with stub)対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
OpenTollGate/tollgate-module-basic-go#726 ·
メンテナーはふだん 1 日以内に返信
-
ux waiting for feedback
難易度 2/5 1〜3時間 初心者へのやさしさ 63/100
evcc-io/evcc#34527 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
phase:v3 type:harness
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信