basecamp/trix · error · TypeError

dirty is not a string, aborting

Error message

dirty is not a string, aborting

What it means

DOMPurify.sanitize accepts a string or a DOM Node. If the input is neither, the library stringifies it (stringifyValue) and, when the result is still not a string, throws this TypeError and aborts. It refuses to guess at non-stringifiable input rather than sanitizing something undefined.

Source

Thrown at action_text-trix/app/assets/javascripts/trix.js:3959

    // eslint-disable-next-line complexity
    DOMPurify.sanitize = function (dirty) {
      let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      let body = null;
      let importedNode = null;
      let currentNode = null;
      let returnNode = null;
      /* Make sure we have a string to sanitize.
        DO NOT return early, as this will return the wrong type if
        the user has requested a DOM object rather than a string */
      IS_EMPTY_INPUT = !dirty;
      if (IS_EMPTY_INPUT) {
        dirty = '<!-->';
      }
      /* Stringify, in case dirty is an object */
      if (typeof dirty !== 'string' && !_isNode(dirty)) {
        dirty = stringifyValue(dirty);
        if (typeof dirty !== 'string') {
          throw typeErrorCreate('dirty is not a string, aborting');
        }
      }
      /* Return dirty HTML if DOMPurify cannot run */
      if (!DOMPurify.isSupported) {
        return dirty;
      }
      /* Assign config vars */
      if (SET_CONFIG) {
        /* Persistent setConfig() path: _parseConfig is skipped, so the sets are
         * not re-derived per call. Restore them from the pristine bindings
         * captured at setConfig() time so a previous call's hook clone (mutated
         * below) does not carry over. */
        ALLOWED_TAGS = SET_CONFIG_ALLOWED_TAGS;
        ALLOWED_ATTR = SET_CONFIG_ALLOWED_ATTR;
      } else {
        _parseConfig(cfg);
      }
      /* Clone the hook-mutable allowlists before the walk whenever an

View on GitHub (pinned to 4700401311)

Solutions

  1. Check the input before calling: ensure it is a string or a Node; coerce with String(dirty) if you intentionally want scalar values sanitized.
  2. Fix the upstream source so the variable is defined before sanitize (guard against undefined/null from fetch, state, or DOM reads).
  3. If passing a DOM node, pass the raw Node, not a wrapper (unwrap jQuery with [0], React refs with .current).
  4. Wrap the call in try/catch if input types are dynamic, and handle the abort path explicitly.

Example fix

// before
const clean = DOMPurify.sanitize(userInput); // userInput may be undefined
// after
if (typeof userInput === 'string') {
  const clean = DOMPurify.sanitize(userInput);
} else if (userInput instanceof Node) {
  const clean = DOMPurify.sanitize(userInput, { RETURN_DOM: true });
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof dirty !== 'string' && !(dirty instanceof Node)) {
  throw new TypeError('sanitize expects an HTML string or a DOM Node; got ' + typeof dirty);
}

Type guard

const isSanitizableInput = (v) => typeof v === 'string' || (v !== null && typeof v === 'object' && v instanceof Node);

Try / catch

let clean;
try {
  clean = DOMPurify.sanitize(dirty);
} catch (e) {
  if (String(e.message).includes('dirty is not a string')) {
    clean = '';
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling DOMPurify.sanitize(undefined), sanitize(null), sanitize({ foo: 1 }) when the object has no useful string representation, or sanitize(someFunction) / sanitize(Symbol()) — anything where typeof dirty !== 'string' && !_isNode(dirty) and stringification does not yield a string.

Common situations: Passing a variable that is unexpectedly undefined because an async fetch or form field came back empty; passing a jQuery/React wrapper object instead of a raw node or HTML string; passing a number/boolean and expecting DOMPurify to coerce it.

Related errors


AI-assisted analysis of basecamp/trix@4700401311 (2026-09-02). Data as JSON: /api/errors/edef35b1bbb3e591. Report an issue: GitHub.