Skip to content

Commit 8680611

Browse files
authored
fix(cli): skip unavailable local vector storage (#5535)
## What changed Local bucket seeding now treats the storage API's local vector service unavailable responses as non-fatal. The command warns, suggests refreshing linked service versions and restarting the local stack, then continues seeding normal storage objects. ## Why `supabase start` can attempt vector bucket seeding for projects whose running local storage service cannot serve vector buckets, for example when local service versions are stale. In that case storage returns `InvalidRequest` with `Vector service not configured`, which should not abort startup for projects that are not actively using vector buckets.
1 parent c94310f commit 8680611

2 files changed

Lines changed: 103 additions & 0 deletions

File tree

apps/cli-go/internal/seed/buckets/buckets.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ func Run(ctx context.Context, projectRef string, interactive bool, fsys afero.Fs
5858
fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "Vector buckets are not available in this project's region yet. Skipping vector bucket seeding.")
5959
return api.UpsertObjects(ctx, utils.Config.Storage.Buckets, utils.NewRootFS(fsys))
6060
}
61+
if isLocalVectorBucketsUnavailable(err) {
62+
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.")
63+
return api.UpsertObjects(ctx, utils.Config.Storage.Buckets, utils.NewRootFS(fsys))
64+
}
6165
return err
6266
}
6367
}
@@ -67,3 +71,14 @@ func Run(ctx context.Context, projectRef string, interactive bool, fsys afero.Fs
6771
func isVectorBucketsFeatureNotEnabled(err error) bool {
6872
return err != nil && strings.Contains(err.Error(), "FeatureNotEnabled")
6973
}
74+
75+
func isLocalVectorBucketsUnavailable(err error) bool {
76+
if err == nil {
77+
return false
78+
}
79+
message := err.Error()
80+
return strings.Contains(message, "Vector service not configured") ||
81+
(strings.Contains(message, "Error status 404:") &&
82+
strings.Contains(message, "Route POST:") &&
83+
strings.Contains(message, "ListVectorBuckets"))
84+
}

apps/cli-go/internal/seed/buckets/buckets_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,21 @@ public = true`
107107
assert.Empty(t, apitest.ListUnmatchedRequests())
108108
})
109109

110+
t.Run("does not call storage API when no buckets are configured", func(t *testing.T) {
111+
t.Cleanup(func() {
112+
utils.Config.Storage.VectorBuckets.Enabled = false
113+
clear(utils.Config.Storage.VectorBuckets.Buckets)
114+
gock.OffAll()
115+
})
116+
utils.Config.Storage.VectorBuckets.Enabled = true
117+
utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{}
118+
119+
err := Run(context.Background(), "", false, afero.NewMemMapFs())
120+
121+
assert.NoError(t, err)
122+
assert.Empty(t, apitest.ListUnmatchedRequests())
123+
})
124+
110125
t.Run("seeds vector buckets locally", func(t *testing.T) {
111126
t.Cleanup(func() {
112127
utils.Config.Storage.VectorBuckets.Enabled = false
@@ -186,6 +201,79 @@ public = true`
186201
assert.Contains(t, stderr, "Vector buckets are not available in this project's region yet")
187202
assert.Empty(t, apitest.ListUnmatchedRequests())
188203
})
204+
205+
t.Run("warns and continues when local vector storage is not configured", func(t *testing.T) {
206+
t.Cleanup(func() {
207+
utils.Config.Storage.VectorBuckets.Enabled = false
208+
clear(utils.Config.Storage.VectorBuckets.Buckets)
209+
gock.OffAll()
210+
})
211+
utils.Config.Storage.VectorBuckets.Enabled = true
212+
utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{
213+
"documents-openai": {},
214+
}
215+
216+
gock.New(utils.Config.Api.ExternalUrl).
217+
Get("/storage/v1/bucket").
218+
Reply(http.StatusOK).
219+
JSON([]storage.BucketResponse{})
220+
gock.New(utils.Config.Api.ExternalUrl).
221+
Post("/storage/v1/vector/ListVectorBuckets").
222+
Reply(http.StatusConflict).
223+
JSON(map[string]any{
224+
"statusCode": http.StatusConflict,
225+
"code": "InvalidRequest",
226+
"error": "InvalidRequest",
227+
"message": "The feature Vector service not configured is not enabled for this resource",
228+
})
229+
230+
stderr := captureStderr(t, func() {
231+
err := Run(context.Background(), "", false, afero.NewMemMapFs())
232+
assert.NoError(t, err)
233+
})
234+
235+
assert.Contains(t, stderr, "WARNING:")
236+
assert.Contains(t, stderr, "Vector buckets are not available in the local storage service")
237+
assert.Contains(t, stderr, "supabase link")
238+
assert.Contains(t, stderr, "restart the local stack")
239+
assert.Empty(t, apitest.ListUnmatchedRequests())
240+
})
241+
242+
t.Run("warns and continues when local vector routes are not registered", func(t *testing.T) {
243+
t.Cleanup(func() {
244+
utils.Config.Storage.VectorBuckets.Enabled = false
245+
clear(utils.Config.Storage.VectorBuckets.Buckets)
246+
gock.OffAll()
247+
})
248+
utils.Config.Storage.VectorBuckets.Enabled = true
249+
utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{
250+
"documents-openai": {},
251+
}
252+
253+
gock.New(utils.Config.Api.ExternalUrl).
254+
Get("/storage/v1/bucket").
255+
Reply(http.StatusOK).
256+
JSON([]storage.BucketResponse{})
257+
gock.New(utils.Config.Api.ExternalUrl).
258+
Post("/storage/v1/vector/ListVectorBuckets").
259+
Reply(http.StatusNotFound).
260+
JSON(map[string]any{
261+
"statusCode": http.StatusNotFound,
262+
"error": "Not Found",
263+
"message": "Route POST:/vector/ListVectorBuckets not found",
264+
})
265+
266+
stderr := captureStderr(t, func() {
267+
err := Run(context.Background(), "", false, afero.NewMemMapFs())
268+
assert.NoError(t, err)
269+
})
270+
271+
assert.Contains(t, stderr, "WARNING:")
272+
assert.Contains(t, stderr, "Vector buckets are not available in the local storage service")
273+
assert.Contains(t, stderr, "supabase link")
274+
assert.Contains(t, stderr, "restart the local stack")
275+
assert.Empty(t, apitest.ListUnmatchedRequests())
276+
})
189277
}
190278

191279
func captureStderr(t *testing.T, run func()) string {

0 commit comments

Comments
 (0)