From cb135fc4e48f334035c2b2b03f4305c89bce84af Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Thu, 11 Jun 2026 20:16:50 +0200 Subject: [PATCH 1/2] Improve error for labs projects that do not provide labs.yml Most repositories in the databrickslabs GitHub org do not ship a labs.yml manifest and thus cannot be installed via the CLI. Installing one of them (e.g. 'databricks labs install brickster') failed with the cryptic 'Error: remote: read labs.yml from GitHub: not found'. Detect the not-found case and explain that the project is not installable with the CLI, pointing at the repository for instructions. Co-authored-by: Isaac --- cmd/labs/project/fetcher.go | 8 ++++++++ cmd/labs/project/fetcher_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 cmd/labs/project/fetcher_test.go diff --git a/cmd/labs/project/fetcher.go b/cmd/labs/project/fetcher.go index c6969ae1ba7..53a9fa85315 100644 --- a/cmd/labs/project/fetcher.go +++ b/cmd/labs/project/fetcher.go @@ -151,6 +151,14 @@ func (f *fetcher) loadRemoteProjectDefinition(cmd *cobra.Command, version string var err error if !offlineInstall { raw, err = github.ReadFileFromRef(ctx, "databrickslabs", f.name, version, "labs.yml") + // Most repositories in the databrickslabs org don't ship a labs.yml manifest + // (e.g. libraries published to package indexes), so a missing file means the + // project isn't installable through the CLI, not that the download failed. + if errors.Is(err, github.ErrNotFound) { + return nil, fmt.Errorf("databrickslabs/%s@%s does not provide labs.yml (%w); "+ + "this project cannot be installed with the Databricks CLI, "+ + "see https://github.com/databrickslabs/%s for instructions", f.name, version, err, f.name) + } if err != nil { return nil, fmt.Errorf("read labs.yml from GitHub: %w", err) } diff --git a/cmd/labs/project/fetcher_test.go b/cmd/labs/project/fetcher_test.go new file mode 100644 index 00000000000..d2eb6427559 --- /dev/null +++ b/cmd/labs/project/fetcher_test.go @@ -0,0 +1,30 @@ +package project_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/databricks/cli/cmd/labs/github" + "github.com/databricks/cli/internal/testcli" + "github.com/stretchr/testify/assert" +) + +func TestInstallerFailsForProjectWithoutLabsYml(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/databrickslabs/blueprint/v0.3.15/labs.yml" { + w.WriteHeader(http.StatusNotFound) + return + } + t.Logf("Requested: %s", r.URL.Path) + t.FailNow() + })) + defer server.Close() + ctx := installerContext(t, server) + + r := testcli.NewRunner(t, ctx, "labs", "install", "blueprint") + _, _, err := r.Run() + assert.ErrorIs(t, err, github.ErrNotFound) + assert.ErrorContains(t, err, "databrickslabs/blueprint@v0.3.15 does not provide labs.yml") + assert.ErrorContains(t, err, "cannot be installed with the Databricks CLI") +} From 6a0d21ce062bb3731c5c2fcedae75c3192ae7e72 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 17 Jun 2026 00:16:23 +0200 Subject: [PATCH 2/2] Address review: cover nonexistent-version case in labs.yml error The not-found error claimed the project 'cannot be installed with the Databricks CLI', but the same 404 also fires for a valid project at a version that does not exist (e.g. 'labs install blueprint@v9.9.9'). Reword to surface both causes, broaden the test to cover both, and add a NEXT_CHANGELOG.md entry. Co-authored-by: Isaac --- NEXT_CHANGELOG.md | 1 + cmd/labs/project/fetcher.go | 12 ++++++----- cmd/labs/project/fetcher_test.go | 34 +++++++++++++++++++++++--------- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 6a15893628e..4beaf2228ed 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -9,6 +9,7 @@ * Increase the SSH server startup timeout from 10 to 45 minutes when a GPU accelerator is requested via `databricks ssh connect --accelerator` ([#5569](https://github.com/databricks/cli/pull/5569)). * Fix authentication falling back to the default profile in `.databrickscfg` when a host is already configured via the environment (e.g. `DATABRICKS_HOST` with `DATABRICKS_TOKEN`) ([#5616](https://github.com/databricks/cli/pull/5616)). * ssh: fix opening remote environment in Cursor, which previously hung on default-extension install and never opened the editor ([#5619](https://github.com/databricks/cli/pull/5619)). +* Improve the error shown when `databricks labs install` cannot find a project's `labs.yml`: the message now explains that either the requested version does not exist or the project is not installable with the CLI, and links to the repository ([#5559](https://github.com/databricks/cli/pull/5559)). ### Bundles diff --git a/cmd/labs/project/fetcher.go b/cmd/labs/project/fetcher.go index 53a9fa85315..395bd10ca54 100644 --- a/cmd/labs/project/fetcher.go +++ b/cmd/labs/project/fetcher.go @@ -151,12 +151,14 @@ func (f *fetcher) loadRemoteProjectDefinition(cmd *cobra.Command, version string var err error if !offlineInstall { raw, err = github.ReadFileFromRef(ctx, "databrickslabs", f.name, version, "labs.yml") - // Most repositories in the databrickslabs org don't ship a labs.yml manifest - // (e.g. libraries published to package indexes), so a missing file means the - // project isn't installable through the CLI, not that the download failed. + // A 404 on labs.yml has two causes we can't tell apart here: the requested + // version doesn't exist, or the repository simply doesn't ship a labs.yml + // (most databrickslabs repos don't, e.g. libraries published to package + // indexes) and so isn't installable through the CLI. Either way it's not a + // download failure, so surface both possibilities instead of the raw error. if errors.Is(err, github.ErrNotFound) { - return nil, fmt.Errorf("databrickslabs/%s@%s does not provide labs.yml (%w); "+ - "this project cannot be installed with the Databricks CLI, "+ + return nil, fmt.Errorf("no labs.yml at databrickslabs/%s@%s (%w); "+ + "either this version does not exist or this project cannot be installed with the Databricks CLI, "+ "see https://github.com/databrickslabs/%s for instructions", f.name, version, err, f.name) } if err != nil { diff --git a/cmd/labs/project/fetcher_test.go b/cmd/labs/project/fetcher_test.go index d2eb6427559..2e93c1f79d9 100644 --- a/cmd/labs/project/fetcher_test.go +++ b/cmd/labs/project/fetcher_test.go @@ -3,6 +3,7 @@ package project_test import ( "net/http" "net/http/httptest" + "strings" "testing" "github.com/databricks/cli/cmd/labs/github" @@ -10,21 +11,36 @@ import ( "github.com/stretchr/testify/assert" ) -func TestInstallerFailsForProjectWithoutLabsYml(t *testing.T) { +func TestInstallerFailsWhenLabsYmlMissing(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/databrickslabs/blueprint/v0.3.15/labs.yml" { + if strings.HasSuffix(r.URL.Path, "/labs.yml") { w.WriteHeader(http.StatusNotFound) return } - t.Logf("Requested: %s", r.URL.Path) - t.FailNow() + t.Errorf("unexpected request: %s", r.URL.Path) + http.Error(w, "unexpected request", http.StatusInternalServerError) })) defer server.Close() ctx := installerContext(t, server) - r := testcli.NewRunner(t, ctx, "labs", "install", "blueprint") - _, _, err := r.Run() - assert.ErrorIs(t, err, github.ErrNotFound) - assert.ErrorContains(t, err, "databrickslabs/blueprint@v0.3.15 does not provide labs.yml") - assert.ErrorContains(t, err, "cannot be installed with the Databricks CLI") + // A 404 on labs.yml has two causes: the repository ships no labs.yml (reached + // here via the only released version, v0.3.15), or the requested version does + // not exist (v9.9.9). Both must produce the same actionable error. + tests := []struct { + name string + ref string + version string + }{ + {name: "released version without labs.yml", ref: "blueprint", version: "v0.3.15"}, + {name: "nonexistent version", ref: "blueprint@v9.9.9", version: "v9.9.9"}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + r := testcli.NewRunner(t, ctx, "labs", "install", tc.ref) + _, _, err := r.Run() + assert.ErrorIs(t, err, github.ErrNotFound) + assert.ErrorContains(t, err, "no labs.yml at databrickslabs/blueprint@"+tc.version) + assert.ErrorContains(t, err, "either this version does not exist or this project cannot be installed with the Databricks CLI") + }) + } }