Skip to content

[bug] @astrojs/cloudflare: custom kv_namespaces get wiped out when SESSION binding is injected #16554

@hunnyboy1217

Description

@hunnyboy1217

Astro Info

Astro                    v6.2.1
Vite                     v7.3.2
Node                     v22.22.2
System                   Linux (x64)
Package Manager          npm
Output                   static
Adapter                  none
Integrations             none

If this issue only occurs in one browser, which browser is a problem?

No response

Describe the Bug

If you have custom KV namespaces in your wrangler config and Astro sessions are enabled (the default), your bindings silently disappear at runtime. The cloudflare integration injects the SESSION KV binding by returning a brand-new array — it doesn't merge with whatever you already had, it just replaces it.

So if your wrangler config has:

[[kv_namespaces]]
binding = "MY_KV"
id = "abc123"

[[kv_namespaces]]
binding = "CACHE"
id = "def456"

After the customizer runs, the resolved config ends up as:

kv_namespaces: [{ binding: 'SESSION' }]
// MY_KV and CACHE are gone

No error, no warning — the worker just can't see those bindings anymore. This happens in both the top-level config and previews since they both go through the same getNonInheritableBindings helper.

What makes this tricky to catch is that the existing test in wrangler.test.ts actually bakes in the broken behavior. It passes OTHER_KV as input but only checks that SESSION shows up in the output — it never checks whether OTHER_KV survived:

// wrangler.test.ts:60–67
it('adds SESSION binding when other KV bindings exist but not the session one', () => {
    const result = customizer({
        kv_namespaces: [{ binding: 'OTHER_KV', id: 'other-id' }],
    });
    // OTHER_KV is gone from result, but the test doesn't notice
    assert.deepEqual(result.kv_namespaces, [{ binding: DEFAULT_SESSION_KV_BINDING_NAME }]);
});

Relevant code in packages/integrations/cloudflare/src/wrangler.ts lines 44–51:

kv_namespaces:
    !needsSessionKVBinding || hasSessionBinding
        ? undefined
        : [
              {
                  binding: sessionKVBindingName,  // fresh array, no merge with existing
              },
          ],

What's the expected result?

Any existing kv_namespaces should be kept when SESSION is injected. The fix is straightforward — just spread the existing list before appending:

kv_namespaces:
    !needsSessionKVBinding || hasSessionBinding
        ? undefined
        : [
              ...(nonInheritableConfig?.kv_namespaces ?? []),
              { binding: sessionKVBindingName },
          ],

Same change needed for the previews path.

Link to Minimal Reproducible Example

https://github.com/Hunnyboy1217/kv-namespaces-drop-repro.git

Participation

  • I am willing to submit a pull request for this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    - P3: minor bugAn edge case that only affects very specific usage (priority)pkg: cloudflareRelated to the Cloudflare adapter

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions