vitejs/vite · error · Error

The value passed to "base" option was malformed. It should…

Error message

The value passed to "base" option was malformed. It should be a valid URL.

What it means

Vite calls decodeBase to URI-decode the resolved base path so encoded characters (e.g. %20) work in URLs. If base contains a malformed percent sequence, decodeURI throws and Vite re-wraps it as 'base option was malformed'. The check assumes base already passed URL parsing upstream; only the decode step can fail.

Solutions

  1. Correct the percent-encoding in base (use %20 for space, %2F for slash, etc.).
  2. Avoid raw '%' in base; if a literal percent is needed, encode it as %25.
  3. If base is computed, validate it with new URL(...) before passing to Vite.

Example fix

// before
export default defineConfig({ base: '/my%path%' })
// after
export default defineConfig({ base: '/my%25path%25' }) // literal percent signs
Defensive patterns

Strategy: validation

Validate before calling

function isValidBase(base) {
  try { decodeURI(base); new URL(base, 'http://vite.dev'); return true; } catch { return false; }
}

Type guard

function isDecodableBase(base) {
  try { decodeURI(base); return true; } catch { return false; }
}

Prevention

When it happens

Trigger: Setting config.base to a string containing an invalid percent escape, e.g. '%%', '%zz', or a stray '%' followed by non-hex characters.

Common situations: Dynamic base computation that concatenates a percent-encoded segment incorrectly; copy-pasting a URL with a broken fragment; build scripts that template base from env vars containing stray %.

Understand the failure class

Related errors


AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11). Data as JSON: /api/errors/3254c4bfe863192d. Report an issue: GitHub.

Appendix: source

Thrown at packages/vite/src/node/config.ts:2340

  }

  // parse base when command is serve or base is not External URL
  if (!isBuild || !isExternal) {
    base = new URL(base, 'http://vite.dev').pathname
    // ensure leading slash
    if (base[0] !== '/') {
      base = '/' + base
    }
  }

  return base
}

function decodeBase(base: string): string {
  try {
    return decodeURI(base)
  } catch {
    throw new Error(
      'The value passed to "base" option was malformed. It should be a valid URL.',
    )
  }
}

export function sortUserPlugins(
  plugins: (Plugin | Plugin[])[] | undefined,
): [Plugin[], Plugin[], Plugin[]] {
  const prePlugins: Plugin[] = []
  const postPlugins: Plugin[] = []
  const normalPlugins: Plugin[] = []

  if (plugins) {
    plugins.flat().forEach((p) => {
      if (p.enforce === 'pre') prePlugins.push(p)
      else if (p.enforce === 'post') postPlugins.push(p)
      else normalPlugins.push(p)
    })

View on GitHub (pinned to b4d66fee14)