google/zx · critical · Fail

No quote function is defined: ${Fail.DOCS_URL}/quotes

Error message

No quote function is defined: ${Fail.DOCS_URL}/quotes

What it means

Thrown by ProcessPromise.build() when $.quote is undefined. zx needs a shell-specific quoting function to safely interpolate arguments; quote is normally assigned by useBash()/usePwsh()/usePowerShell(). At module load zx calls useBash(); if bash is not found, which.sync throws and the assignment is skipped inside an empty try/catch, leaving $.quote undefined, so the very first `$` call hits this guard. See /quotes.

Source

Thrown at src/core.ts:296

    if (snapshot) {
      this._snapshot = snapshot
      this._resolve = resolve!
      this._reject = reject!
      if (snapshot.halt) this._stage = 'halted'
      try {
        this.build()
      } catch (err) {
        this.finalize(ProcessOutput.fromError(err as Error), true)
      }
    } else ProcessPromise.disarm(this)
  }
  // prettier-ignore
  private build(): void {
    const $ = this._snapshot
    if (!$.shell)
      throw new Fail(`No shell is available: ${Fail.DOCS_URL}/shell`)
    if (!$.quote)
      throw new Fail(`No quote function is defined: ${Fail.DOCS_URL}/quotes`)
    if ($.pieces.some((p) => p == null))
      throw new Fail(`Malformed command at ${$.from}`)

    $.cmd = buildCmd(
      $.quote!,
      $.pieces as TemplateStringsArray,
      $.args
    ) as string

    if ($[SYNC] && !isString($.cmd))
      throw new Fail('sync mode does not allow async command resolution')
  }
  run(): this {
    ProcessPromise.bus.runBack(this)
    if (this.isRunning() || this.isSettled()) return this // The _run() can be called from a few places.
    this._stage = 'running'

    const self = this

View on GitHub (pinned to 00a2c484e2)

Solutions

  1. Install bash in the environment (`apk add bash`, `apt-get install -y bash`).
  2. Switch to a configured shell helper: `usePwsh()` or `usePowerShell()`.
  3. Provide both shell and quote explicitly: `$.shell = '/bin/sh'; $.quote = quote` (import the built-in `quote` for POSIX shells).
  4. Set `$.shell = true` and ensure bash exists so auto-detection works.

Example fix

// Dockerfile before
FROM alpine
RUN npm i -g zx
// after: install bash so useBash() succeeds and $.quote gets set
FROM alpine
RUN apk add --no-cache bash && npm i -g zx
Defensive patterns

Strategy: validation

Validate before calling

import { $, quote } from 'zx'

function ensureQuoteFn(): void {
  if (typeof $.quote !== 'function') {
    // POSIX fallback when bash is absent
    $.shell = $.shell || '/bin/sh'
    $.quote = quote
  }
}

ensureQuoteFn()

Type guard

const hasQuoteFn = (v: unknown): v is (s: string) => string =>
  typeof v === 'function'

Try / catch

import { Fail, quote } from 'zx'
try {
  await $`echo hi`
} catch (e) {
  if (e instanceof Fail && /No quote function is defined/.test(e.message)) {
    $.quote = quote
    await $`echo hi`
  } else throw e
}

Prevention

When it happens

Trigger: Running zx on a system without bash (Alpine, distroless, minimal containers) with no other shell configured; setting $.shell to a custom path without also providing $.quote; overriding Options in a way that drops the quote key.

Common situations: Docker `alpine`/scratch images without bash; CI on a minimal image; custom shells (fish, sh, busybox) set via $.shell but with no matching quote function; Node runtimes where bash is not on PATH.

Related errors


AI-assisted analysis of google/zx@00a2c484e2 (2026-08-13). Data as JSON: /api/errors/43b8745fc747a352. Report an issue: GitHub.