google/zx · error · Fail
Inappropriate usage. Apply $ instead of direct instantiation
Error message
Inappropriate usage. Apply $ instead of direct instantiation.
What it means
Thrown by the static disarm() trap installed on a ProcessPromise that was constructed directly via `new ProcessPromise(executor)` instead of through the `$` tagged-template factory. When the constructor receives an executor lacking the internal SHOT snapshot, it disarms every non-Promise method so any access throws, steering you toward using `$`. This prevents callers from treating ProcessPromise like a plain subclassable Promise.
Source
Thrown at src/core.ts:845
this._stdin.write(data, encoding, cb)
return this
}
private end(chunk: any, cb: any) {
this._stdin.end(chunk, cb)
return this
}
private removeListener(event: string, cb: any) {
this._stdin.removeListener(event, cb)
return this
}
// prettier-ignore
private static disarm(p: ProcessPromise, toggle = true): void {
Object.getOwnPropertyNames(ProcessPromise.prototype).forEach(k => {
if (k in Promise.prototype) return
if (!toggle) { Reflect.deleteProperty(p, k); return }
Object.defineProperty(p, k, { configurable: true, get() {
throw new Fail('Inappropriate usage. Apply $ instead of direct instantiation.')
}})
})
}
}
type ProcessDto = {
code: number | null
signal: NodeJS.Signals | null
duration: number
error: any
from: string
store: TSpawnStore
delimiter?: string | RegExp
}
export class ProcessOutput extends Error {
private readonly _dto!: ProcessDto
cause!: Error | nullView on GitHub (pinned to 00a2c484e2)
Solutions
- Always create commands via the tagged template: `` $`echo ok` `` or `` $({opts})`cmd` ``.
- If you need a resolved ProcessOutput, construct ProcessOutput directly rather than a fake ProcessPromise.
- Do not subclass or reflectively instantiate ProcessPromise.
Example fix
// before new ProcessPromise((resolve) => resolve()) // after $`echo ok`
Defensive patterns
Strategy: type-guard
Validate before calling
// Reject direct construction at the type layer; always route through $.
function makeCommand(tpl: TemplateStringsArray, ...args: unknown[]) {
return $(tpl, ...args)
} Type guard
import { ProcessPromise } from 'zx'
const madeByFactory = (p: ProcessPromise): boolean =>
typeof (p as any).stage === 'string' // disarmed instances throw on stage access Try / catch
try {
// never do: new ProcessPromise(...)
await $`echo ok`
} catch (e) {
if (e instanceof Fail && /Apply \$ instead of direct instantiation/.test(e.message)) {
throw new Error('Use the $ tagged template, not new ProcessPromise()')
}
throw e
} Prevention
- Never call `new ProcessPromise(...)` — always use the `$` tagged template.
- Do not subclass or reflectively instantiate ProcessPromise.
- For pre-resolved outputs, build ProcessOutput directly instead of faking a ProcessPromise.
When it happens
Trigger: `new ProcessPromise((res) => res())`; subclassing or reflectively instantiating ProcessPromise; copy-pasting a Promise constructor pattern.
Common situations: Treating ProcessPromise as a normal Promise; library code that instantiates it directly; attempting to wrap or extend the class.
Related errors
AI-assisted analysis of google/zx@00a2c484e2 (2026-08-13).
Data as JSON: /api/errors/1b57fbb8dc1f9746.
Report an issue: GitHub.