Skip to main content

Azure Static Web Apps: Fixing the "App Content Too Large" Deployment Error

This document describes the root cause, diagnosis, and fix for the Azure Static Web Apps (SWA) deployment failure on rksp-vtk-react when publishing to the Free tier.


Symptom

During the Build And Deploy step in GitHub Actions, deployment failed with:

The content server has rejected the request with: BadRequest
Reason: The size of the app content was too large. The limit for this Static Web App is 262144000 bytes.

262,144,000 bytes = 250 MB, which is the app content size limit on the Azure SWA Free plan.


Initial Assumption (Incorrect)

Because this is a large application (VTK.js, ITK-wasm, CopilotKit, 22,000+ Vite modules), we initially suspected the built dist/ folder was too large and needed trimming or a plan upgrade.

Local investigation showed that was not the problem.


Actual Root Cause

The deployment was uploading far more than the built app.

ContentApproximate size
dist/ (production build output)~25 MB
node_modules/~629 MB
Full repo (source + node_modules)~665 MB

The built app was well under the 250 MB limit. Azure rejected the upload because the workflow was configured to deploy from the repository root instead of dist/ only, so node_modules and other source files were included in the upload payload.

Why the workflow misconfiguration happened

We build in GitHub Actions and skip Azure's Oryx build with skip_app_build: true. That requires a specific SWA workflow configuration documented by Microsoft. Our workflow used an incorrect combination:

# INCORRECT — uploads from repo root, includes node_modules
app_location: "/"
output_location: "dist"
skip_app_build: true
app_artifact_location: "dist"

With app_location: "/" and skip_app_build: true, the deploy action treated the repo root as the app content folder. Setting output_location: "dist" does not restrict the upload to dist/ in this mode. The app_artifact_location input is an alias for output_location and did not correct the behavior when app_location pointed at /.

Reference: Azure Static Web Apps build configuration — Skip building front-end app


The Fix

1. Correct SWA deploy configuration (primary fix)

When building in a prior GitHub Actions step, configure the deploy step as follows:

- name: Build app
run: node .yarn/releases/yarn-4.13.0.cjs build

- name: Report build artifact size
run: |
echo "Deploy artifact summary:"
du -sh dist
find dist -type f | wc -l

- name: Remove node_modules before deploy
run: rm -rf node_modules

- name: Build And Deploy
uses: Azure/static-web-apps-deploy@v1
with:
action: "upload"
app_location: "dist" # Deploy ONLY the build output
output_location: "" # Required when skip_app_build is true
skip_app_build: true
api_location: ""
# ... tokens and other settings

Key rules when using skip_app_build: true:

SettingValuePurpose
app_location"dist"Points at the pre-built static files
output_location"" (empty string)Required by Azure when skipping Oryx build
skip_app_buildtrueTells Azure not to run its own build

Do not set app_location: "/" when pre-building in CI.

2. Remove node_modules before deploy (safety net)

Even with the correct app_location, deleting node_modules before the deploy step prevents accidental inclusion if configuration changes in the future:

- name: Remove node_modules before deploy
run: rm -rf node_modules

3. Add a deploy artifact size check (observability)

A reporting step makes future size issues easy to spot in CI logs:

- name: Report build artifact size
run: |
echo "Deploy artifact summary:"
du -sh dist
find dist -type f | wc -l

Expected output after a successful build:

Deploy artifact summary:
25M dist
519

If this step ever shows hundreds of MB or thousands of files, something is wrong with the build or deploy configuration.


These changes were made during the same deployment troubleshooting effort. They solved other CI issues or improved performance but were not required to fix the 250 MB limit error.

Pre-build in GitHub Actions instead of Azure Oryx

The app is too heavy for Azure's default Oryx build environment (memory limits, Yarn version mismatch). We build in GitHub Actions and upload the result:

  1. Install dependencies with Yarn 4 (node .yarn/releases/yarn-4.13.0.cjs install --immutable)
  2. Build with increased Node heap (NODE_OPTIONS: --max-old-space-size=8192 + swap space on the runner)
  3. Deploy only dist/

JavaScript heap out of memory during build

GitHub-hosted runners have ~7 GB RAM. Our Vite build needs more for 22,000+ modules. Mitigations:

  • NODE_OPTIONS: --max-old-space-size=8192 at the job level
  • 10 GB swap added before install/build
  • maxParallelFileOps: 1 in vite.config.mjs to reduce Rollup peak memory

Yarn 4 in CI

The project uses Yarn 4.13.0 (packageManager field + Berry lockfile). CI invokes the committed binary directly to avoid the runner's global Yarn 1.22.x:

run: node .yarn/releases/yarn-4.13.0.cjs install --immutable
run: node .yarn/releases/yarn-4.13.0.cjs build

CopilotKit lazy loading (runtime optimization)

CopilotKit (including shiki syntax highlighting and mermaid) was moved out of the main entry bundle and lazy-loaded from the dashboard layout only:

  • New component: src/components/CopilotAppShell.jsx
  • Removed CopilotKit from src/index.jsx
  • Dashboard layout lazy-loads CopilotAppShell and AiAssistantDialog

This improves initial page load (login no longer downloads the Copilot stack) but does not materially change total deploy size—all chunks still ship in dist/.


What's Actually in dist/ (~25 MB)

For reference, a typical production build contains:

CategoryApprox. sizeNotes
JavaScript bundles~19 MBVTK, CopilotKit/shiki, MUI, app code
Static images~4 MBLogos, template mock images in public/static/
Fonts (KaTeX, etc.)~1 MBPulled in by CopilotKit markdown rendering
CSS, HTML, misc<1 MB

The largest single chunk is the CopilotKit-related bundle (~4–6 MB minified). Even with all chunks included, total deploy size is roughly 10× smaller than the Free tier limit.


Verification Checklist

After applying the fix, confirm in GitHub Actions:

  1. Build completes without OOM errors.
  2. Report build artifact size shows ~25 MB and ~500 files (not hundreds of MB or 10,000+ files).
  3. Build And Deploy succeeds without the BadRequest size error.
  4. The deployed site loads correctly at the SWA URL.

Locally, you can sanity-check before pushing:

yarn build
Get-ChildItem dist -Recurse -File | Measure-Object -Property Length -Sum

Expected total: ~25–30 MB.


Do We Need the Standard Plan?

No, for this specific error. The Free tier 250 MB limit is sufficient when only dist/ is deployed.

Consider upgrading to Standard only if:

  • The actual dist/ output genuinely grows beyond 250 MB, or
  • You need other Standard-tier features (custom domains, SLA, higher limits, etc.)

Troubleshooting Guide

Error returns after a workflow edit

Check that app_location is still "dist" and output_location is still "". A revert to app_location: "/" will bring the error back.

du -sh dist shows >250 MB in CI

The build output itself is too large. Investigate:

  • Accidental inclusion of large assets in public/
  • Source maps enabled in production (build.sourcemap: true in Vite)
  • Unintended files copied into dist/

Deploy succeeds but site is broken

Ensure public/_redirects, staticwebapp.config.json (if used), and SPA routing assets are present in dist/. Vite copies public/ contents into dist/ during build.

File count limit error (15,000 files)

A separate SWA limit. If encountered, check that node_modules is not being uploaded. Same root cause as the size error.


Summary

ProblemRoot causeFix
"App content too large" (250 MB)Deploy uploaded repo root + node_modules (~665 MB), not dist/ (~25 MB)Set app_location: "dist", output_location: "", skip_app_build: true; remove node_modules before deploy

The application was never too large for Azure SWA Free—the deployment package was pointed at the wrong folder.


References