Skip to content

quasar build: numeric-looking process.env values emitted as unquoted defines, breaking rolldown (INVALID_DEFINE_CONFIG) #18352

Description

@JoseGoncalves

What kind of issue is this?

Bug — @quasar/app-vite (Vite/rolldown build).

Versions

  • @quasar/app-vite: 3.0.1
  • quasar: 2.21.1
  • vite (rolldown-vite): 8.1.3
  • rolldown: 1.1.4
  • Node: 24

What happens

quasar build aborts while compiling quasar.config.js with:

App • Compiling quasar.config.js (no env prefix; no dotenv files)
Error: Build failed with 1 error:
[INVALID_DEFINE_CONFIG] Invalid characters after number
    at .../node_modules/rolldown/dist/shared/error-*.mjs
    at #build (.../@quasar/app-vite/lib/quasar-config-file.js:641:7)
 App • ⚠️  FAIL  Could not compile the quasar.config file because it has errors.

The failure depends only on the values present in process.env at build time, not on any user code. It surfaced for us in CI because GitLab sets CI_COMMIT_SHORT_SHA, and for one commit its value was 024766e0 — which is why it was intermittent and never reproduced locally.

Minimal reproduction

Any project, no dotenv files, default (empty) quasarConfEnv prefix:

FOO=024766e0 quasar build     # 024766e0 -> Number() === 24766 (an 'e0' exponent), but not a valid bare literal
# or, even simpler:
FOO=08     quasar build       # Number('08') === 8, but `08` is an invalid numeric literal in strict/module code
FOO=0755   quasar build       # Number('0755') === 755, but `0755` is a legacy octal literal (SyntaxError in ESM)

All fail with [INVALID_DEFINE_CONFIG] Invalid characters after number. A "normal" string value (e.g. FOO=abc123, Number()NaN) builds fine because it gets quoted.

Root cause

When compiling quasar.config.js, the define map is built from env vars. With the default empty prefix, all of process.env is included (lib/utils/env.jsgetFileEnvResult returns { ...processEnv } when there are no dotenv files; the empty-prefix regex matches every key). Each value is then stringified by getStrDefineValue (lib/utils/env.js):

function getStrDefineValue(value) {
  if (asIsList.includes(value)) return value
  const trimmed = value.trim()
  return trimmed !== '' && !Number.isNaN(Number(trimmed))
    ? value                 // <-- emitted RAW / unquoted
    : JSON.stringify(value)
}

Number() is far more permissive than JavaScript's numeric-literal grammar. Values like 024766e0, 08, 0755, 0x-less hex-ish strings, whitespace-padded numbers, etc. are not NaN under Number(), so they are spliced into the rolldown define map unquoted, producing an invalid expression that the bundler correctly rejects.

Two smaller points in the same function:

  • It returns the untrimmed value in the numeric branch, so " 3000 " would also be emitted raw.
  • Emitting numeric env values as unquoted number literals also diverges from Vite semantics, where import.meta.env.* values are strings.

Expected

A valid string env var should never break the build. Env var values are always strings and should be quoted.

Suggested fix

Either:

  1. Always JSON.stringify env values (simplest, matches Vite's string semantics), or

  2. If numeric passthrough is intentional, gate it on a strict numeric-literal check instead of Number(), e.g. a canonical-number regex, and use trimmed rather than value:

    const NUMERIC_LITERAL_RE = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/
    return NUMERIC_LITERAL_RE.test(trimmed) ? trimmed : JSON.stringify(value)

Separately, consider whether injecting all of process.env (empty default prefix) into the quasar.config compile is desirable, since it can leak unrelated/sensitive CI variables into build-time defines.

Workaround

Set a non-empty config-env prefix so only intended vars reach the config compile (package.json):

{
  "quasarCli": {
    "quasarConfEnv": { "prefix": "MYAPP_CONF_" }
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    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