From 55a9101af9fc3724ec51b6000b1ed6ac69c9cb4a Mon Sep 17 00:00:00 2001 From: Julien Goux Date: Wed, 10 Jun 2026 17:46:40 +0200 Subject: [PATCH] fix(cli): skip unavailable local vector storage --- apps/cli-go/internal/seed/buckets/buckets.go | 15 ++++ .../internal/seed/buckets/buckets_test.go | 88 +++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/apps/cli-go/internal/seed/buckets/buckets.go b/apps/cli-go/internal/seed/buckets/buckets.go index 9fe16f0b05..e077f5e716 100644 --- a/apps/cli-go/internal/seed/buckets/buckets.go +++ b/apps/cli-go/internal/seed/buckets/buckets.go @@ -58,6 +58,10 @@ func Run(ctx context.Context, projectRef string, interactive bool, fsys afero.Fs fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "Vector buckets are not available in this project's region yet. Skipping vector bucket seeding.") return api.UpsertObjects(ctx, utils.Config.Storage.Buckets, utils.NewRootFS(fsys)) } + if isLocalVectorBucketsUnavailable(err) { + fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "Vector buckets are not available in the local storage service. If this project is linked, run `supabase link` to update service versions, then restart the local stack. Skipping vector bucket seeding.") + return api.UpsertObjects(ctx, utils.Config.Storage.Buckets, utils.NewRootFS(fsys)) + } return err } } @@ -67,3 +71,14 @@ func Run(ctx context.Context, projectRef string, interactive bool, fsys afero.Fs func isVectorBucketsFeatureNotEnabled(err error) bool { return err != nil && strings.Contains(err.Error(), "FeatureNotEnabled") } + +func isLocalVectorBucketsUnavailable(err error) bool { + if err == nil { + return false + } + message := err.Error() + return strings.Contains(message, "Vector service not configured") || + (strings.Contains(message, "Error status 404:") && + strings.Contains(message, "Route POST:") && + strings.Contains(message, "ListVectorBuckets")) +} diff --git a/apps/cli-go/internal/seed/buckets/buckets_test.go b/apps/cli-go/internal/seed/buckets/buckets_test.go index 7c5fe52ec3..9527d3f554 100644 --- a/apps/cli-go/internal/seed/buckets/buckets_test.go +++ b/apps/cli-go/internal/seed/buckets/buckets_test.go @@ -107,6 +107,21 @@ public = true` assert.Empty(t, apitest.ListUnmatchedRequests()) }) + t.Run("does not call storage API when no buckets are configured", func(t *testing.T) { + t.Cleanup(func() { + utils.Config.Storage.VectorBuckets.Enabled = false + clear(utils.Config.Storage.VectorBuckets.Buckets) + gock.OffAll() + }) + utils.Config.Storage.VectorBuckets.Enabled = true + utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{} + + err := Run(context.Background(), "", false, afero.NewMemMapFs()) + + assert.NoError(t, err) + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) + t.Run("seeds vector buckets locally", func(t *testing.T) { t.Cleanup(func() { utils.Config.Storage.VectorBuckets.Enabled = false @@ -186,6 +201,79 @@ public = true` assert.Contains(t, stderr, "Vector buckets are not available in this project's region yet") assert.Empty(t, apitest.ListUnmatchedRequests()) }) + + t.Run("warns and continues when local vector storage is not configured", func(t *testing.T) { + t.Cleanup(func() { + utils.Config.Storage.VectorBuckets.Enabled = false + clear(utils.Config.Storage.VectorBuckets.Buckets) + gock.OffAll() + }) + utils.Config.Storage.VectorBuckets.Enabled = true + utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{ + "documents-openai": {}, + } + + gock.New(utils.Config.Api.ExternalUrl). + Get("/storage/v1/bucket"). + Reply(http.StatusOK). + JSON([]storage.BucketResponse{}) + gock.New(utils.Config.Api.ExternalUrl). + Post("/storage/v1/vector/ListVectorBuckets"). + Reply(http.StatusConflict). + JSON(map[string]any{ + "statusCode": http.StatusConflict, + "code": "InvalidRequest", + "error": "InvalidRequest", + "message": "The feature Vector service not configured is not enabled for this resource", + }) + + stderr := captureStderr(t, func() { + err := Run(context.Background(), "", false, afero.NewMemMapFs()) + assert.NoError(t, err) + }) + + assert.Contains(t, stderr, "WARNING:") + assert.Contains(t, stderr, "Vector buckets are not available in the local storage service") + assert.Contains(t, stderr, "supabase link") + assert.Contains(t, stderr, "restart the local stack") + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) + + t.Run("warns and continues when local vector routes are not registered", func(t *testing.T) { + t.Cleanup(func() { + utils.Config.Storage.VectorBuckets.Enabled = false + clear(utils.Config.Storage.VectorBuckets.Buckets) + gock.OffAll() + }) + utils.Config.Storage.VectorBuckets.Enabled = true + utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{ + "documents-openai": {}, + } + + gock.New(utils.Config.Api.ExternalUrl). + Get("/storage/v1/bucket"). + Reply(http.StatusOK). + JSON([]storage.BucketResponse{}) + gock.New(utils.Config.Api.ExternalUrl). + Post("/storage/v1/vector/ListVectorBuckets"). + Reply(http.StatusNotFound). + JSON(map[string]any{ + "statusCode": http.StatusNotFound, + "error": "Not Found", + "message": "Route POST:/vector/ListVectorBuckets not found", + }) + + stderr := captureStderr(t, func() { + err := Run(context.Background(), "", false, afero.NewMemMapFs()) + assert.NoError(t, err) + }) + + assert.Contains(t, stderr, "WARNING:") + assert.Contains(t, stderr, "Vector buckets are not available in the local storage service") + assert.Contains(t, stderr, "supabase link") + assert.Contains(t, stderr, "restart the local stack") + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) } func captureStderr(t *testing.T, run func()) string {