From 7bfc5fe1398bc09fd8a0d734fb750c54700ba142 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Sat, 28 Feb 2026 09:53:49 -0500 Subject: [PATCH 1/5] fix: set buildOutput default before runHookConfigDone in dev mode (#15701) Server islands in dev mode with adapters that don't set adapterFeatures.buildOutput (like @astrojs/netlify) were broken because buildOutput was reset to 'static' after runHookConfigDone already set it to 'server' via setAdapter(). This moves the default assignment before runHookConfigDone, matching the build path ordering. --- packages/astro/src/core/dev/container.ts | 11 ++++--- packages/astro/test/server-islands.test.js | 38 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts index 46ff4fe47960..002fda9c1378 100644 --- a/packages/astro/src/core/dev/container.ts +++ b/packages/astro/src/core/dev/container.ts @@ -78,6 +78,13 @@ export async function createContainer({ .map((r) => r.clientEntrypoint) .filter(Boolean) as string[]; + // Set the initial buildOutput default before runHookConfigDone, so that + // setAdapter() inside astro:config:done can upgrade it to 'server'. + // This matches the ordering in the build path (packages/astro/src/core/build/index.ts). + if (!settings.adapter?.adapterFeatures?.buildOutput) { + settings.buildOutput = getPrerenderDefault(settings.config) ? 'static' : 'server'; + } + // Create the route manifest already outside of Vite so that `runHookConfigDone` can use it to inform integrations of the build output await runHookConfigDone({ settings, logger, command: 'dev' }); @@ -94,10 +101,6 @@ export async function createContainer({ dev: true, }, ); - // If the adapter explicitly set a buildOutput, don't override it - if (!settings.adapter?.adapterFeatures?.buildOutput) { - settings.buildOutput = getPrerenderDefault(settings.config) ? 'static' : 'server'; - } const viteConfig = await createVite( { server: { host, headers, open, allowedHosts }, diff --git a/packages/astro/test/server-islands.test.js b/packages/astro/test/server-islands.test.js index 7ca88ee2257d..f519555e19b9 100644 --- a/packages/astro/test/server-islands.test.js +++ b/packages/astro/test/server-islands.test.js @@ -394,6 +394,44 @@ describe('Server islands', () => { }); }); + describe('dev with adapter that does not set buildOutput', () => { + let devServer; + /** @type {import('./test-utils').Fixture} */ + let devFixture; + + before(async () => { + // Use an adapter that does NOT set adapterFeatures.buildOutput, + // like @astrojs/netlify. This triggers the bug in container.ts where + // buildOutput is reset to 'static' after runHookConfigDone sets it to 'server'. + devFixture = await loadFixture({ + root: './fixtures/server-islands/hybrid', + adapter: testAdapter({ + extendAdapter: { + adapterFeatures: { + // Explicitly omit buildOutput to mimic Netlify adapter + }, + }, + }), + }); + devServer = await devFixture.startDevServer(); + }); + + after(async () => { + await devServer?.stop(); + }); + + it('can fetch the server island endpoint in dev mode', async () => { + const res = await devFixture.fetch('/'); + assert.equal(res.status, 200); + const html = await res.text(); + // Extract the server island fetch URL from the rendered page + const fetchMatch = html.match(/fetch\('(\/_server-islands\/Island[^']*)/); + assert.ok(fetchMatch, 'should have a server island fetch URL'); + const islandRes = await devFixture.fetch(fetchMatch[1]); + assert.equal(islandRes.status, 200, 'server island endpoint should return 200, not GetStaticPathsRequired error'); + }); + }); + describe('with no adapter', () => { let devServer; From fa1aa8751a326718d519de89ea088127295e622c Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Sat, 28 Feb 2026 10:01:38 -0500 Subject: [PATCH 2/5] add changeset --- .changeset/fix-server-island-dev-build-output.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/fix-server-island-dev-build-output.md diff --git a/.changeset/fix-server-island-dev-build-output.md b/.changeset/fix-server-island-dev-build-output.md new file mode 100644 index 000000000000..ed82e4b4b89e --- /dev/null +++ b/.changeset/fix-server-island-dev-build-output.md @@ -0,0 +1,7 @@ +--- +'astro': patch +--- + +Fixes server islands returning a 500 error in dev mode for adapters that do not set `adapterFeatures.buildOutput` (e.g. `@astrojs/netlify`) + +The `/_server-islands/[name]` endpoint was incorrectly requiring `getStaticPaths()` because `settings.buildOutput` was being reset to `'static'` after the adapter had already set it to `'server'`. From 1ba82eeda6f91f754c564574bda69f3ce527df8e Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Mon, 2 Mar 2026 08:33:35 -0500 Subject: [PATCH 3/5] Update .changeset/fix-server-island-dev-build-output.md Co-authored-by: Florian Lefebvre --- .changeset/fix-server-island-dev-build-output.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.changeset/fix-server-island-dev-build-output.md b/.changeset/fix-server-island-dev-build-output.md index ed82e4b4b89e..fff0dbce6f05 100644 --- a/.changeset/fix-server-island-dev-build-output.md +++ b/.changeset/fix-server-island-dev-build-output.md @@ -3,5 +3,3 @@ --- Fixes server islands returning a 500 error in dev mode for adapters that do not set `adapterFeatures.buildOutput` (e.g. `@astrojs/netlify`) - -The `/_server-islands/[name]` endpoint was incorrectly requiring `getStaticPaths()` because `settings.buildOutput` was being reset to `'static'` after the adapter had already set it to `'server'`. From e006c93999c3bb9698d8db09cee9419a57acc546 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Mon, 2 Mar 2026 09:08:04 -0500 Subject: [PATCH 4/5] Simply test --- packages/astro/test/server-islands.test.js | 44 ++++++++-------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/packages/astro/test/server-islands.test.js b/packages/astro/test/server-islands.test.js index f519555e19b9..c8deffc8105d 100644 --- a/packages/astro/test/server-islands.test.js +++ b/packages/astro/test/server-islands.test.js @@ -394,42 +394,30 @@ describe('Server islands', () => { }); }); - describe('dev with adapter that does not set buildOutput', () => { - let devServer; - /** @type {import('./test-utils').Fixture} */ - let devFixture; - - before(async () => { - // Use an adapter that does NOT set adapterFeatures.buildOutput, - // like @astrojs/netlify. This triggers the bug in container.ts where - // buildOutput is reset to 'static' after runHookConfigDone sets it to 'server'. - devFixture = await loadFixture({ - root: './fixtures/server-islands/hybrid', - adapter: testAdapter({ - extendAdapter: { - adapterFeatures: { - // Explicitly omit buildOutput to mimic Netlify adapter - }, - }, - }), - }); - devServer = await devFixture.startDevServer(); - }); - - after(async () => { - await devServer?.stop(); + it('can fetch the server island endpoint in dev with adapter that does not set buildOutput', async () => { + // Use an adapter that does NOT set adapterFeatures.buildOutput, + // like @astrojs/netlify. This triggers the bug in container.ts where + // buildOutput is reset to 'static' after runHookConfigDone sets it to 'server'. + const devFixture = await loadFixture({ + root: './fixtures/server-islands/hybrid', + adapter: testAdapter({ + extendAdapter: { + adapterFeatures: {}, + }, + }), }); - - it('can fetch the server island endpoint in dev mode', async () => { + const devServer = await devFixture.startDevServer(); + try { const res = await devFixture.fetch('/'); assert.equal(res.status, 200); const html = await res.text(); - // Extract the server island fetch URL from the rendered page const fetchMatch = html.match(/fetch\('(\/_server-islands\/Island[^']*)/); assert.ok(fetchMatch, 'should have a server island fetch URL'); const islandRes = await devFixture.fetch(fetchMatch[1]); assert.equal(islandRes.status, 200, 'server island endpoint should return 200, not GetStaticPathsRequired error'); - }); + } finally { + await devServer.stop(); + } }); describe('with no adapter', () => { From 9490d72f063ee2b8906135642fc5c2a60ed8242c Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Mon, 2 Mar 2026 09:24:03 -0500 Subject: [PATCH 5/5] Update regex method --- packages/astro/test/server-islands.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/test/server-islands.test.js b/packages/astro/test/server-islands.test.js index c8deffc8105d..693ad93b14bb 100644 --- a/packages/astro/test/server-islands.test.js +++ b/packages/astro/test/server-islands.test.js @@ -411,7 +411,7 @@ describe('Server islands', () => { const res = await devFixture.fetch('/'); assert.equal(res.status, 200); const html = await res.text(); - const fetchMatch = html.match(/fetch\('(\/_server-islands\/Island[^']*)/); + const fetchMatch = /fetch\('(\/_server-islands\/Island[^']*)/.exec(html); assert.ok(fetchMatch, 'should have a server island fetch URL'); const islandRes = await devFixture.fetch(fetchMatch[1]); assert.equal(islandRes.status, 200, 'server island endpoint should return 200, not GetStaticPathsRequired error');