Start · Sprachen · JavaScript · Referenz · TransformStreamDefaultController.error()

TransformStreamDefaultController.error()

Methode

Setzt die lesbare und die schreibbare Seite eines <code>TransformStream</code> gleichzeitig in den Fehlerzustand und bricht damit die gesamte Pipeline mit einem Fehlergrund ab.

seit JavaScript Baseline: alle modernen Browser (seit 2022), Node. Kategorie: webapi

Signatur

TransformStreamDefaultController.prototype.error(reason)

Beschreibung

Mit controller.error(reason) signalisiert ein TransformStream, dass die Transformation nicht fortgesetzt werden kann. Beide Seiten gehen sofort in den Fehlerzustand über: Die lesbare Seite (readable) liefert bei jedem weiteren read() eine abgelehnte Promise mit reason. Die schreibbare Seite (writable) lehnt weitere write()-Aufrufe ab. Bereits eingereihte, noch nicht gelesene Chunks werden verworfen.

Aufrufen lässt sich die Methode in den Callbacks start(), transform() und flush() des Transformer-Objekts. Typische Fälle sind ungültige Eingabedaten (z. B. kaputtes JSON in einem NDJSON-Parser), überschrittene Größenlimits oder Protokollverletzungen.

Es gibt einen Unterschied zum bloßen Werfen einer Ausnahme in transform(): Ein throw und eine abgelehnte Promise versetzen den Stream ebenfalls in den Fehlerzustand. error() lässt sich aber auch außerhalb des aktuellen Callbacks aufrufen, etwa aus einem Timer oder Event-Handler, wenn man den Controller zwischenspeichert. Soll nur die lesbare Seite sauber beendet werden, nimmt man stattdessen controller.terminate().

Parameter

Name Typ Default Beschreibung
$reason any undefined Der Fehlergrund. Er wird an alle Leser und Schreiber weitergegeben und erscheint z. B. als abgelehnte Promise von reader.read() oder pipeTo(). Üblich ist eine Error-Instanz.

Rückgabewert

Typ
void
Beschreibung
Gibt undefined zurück. Der Fehler wird über die Promises der Stream-Seiten gemeldet.

Beispiele

Ungültige Zeilen in einem NDJSON-Parser abweisen

const ndjsonParser = () => {
  let buffer = '';
  return new TransformStream({
    transform(chunk, controller) {
      buffer += chunk;
      const lines = buffer.split('\n');
      buffer = lines.pop();
      for (const line of lines) {
        if (!line.trim()) continue;
        try {
          controller.enqueue(JSON.parse(line));
        } catch {
          // Beide Seiten des Streams in den Fehlerzustand setzen
          controller.error(new SyntaxError(`Ungültige Zeile: ${line}`));
          return;
        }
      }
    }
  });
};

const source = new ReadableStream({
  start(c) {
    c.enqueue('{"id":1}\n');
    c.enqueue('kaputt\n');
    c.close();
  }
});

const reader = source.pipeThrough(ndjsonParser()).getReader();
try {
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    console.log('Objekt:', value);
  }
} catch (err) {
  console.error('Abbruch:', err.message);
}
Objekt: { id: 1 } Abbruch: Ungültige Zeile: kaputt

Größenlimit beim Upload durchsetzen

const limitBytes = (max) => {
  let total = 0;
  return new TransformStream({
    transform(chunk, controller) {
      total += chunk.byteLength;
      if (total > max) {
        controller.error(new RangeError(`Limit von ${max} Bytes überschritten`));
        return;
      }
      controller.enqueue(chunk);
    }
  });
};

const data = new Blob([new Uint8Array(2048)]).stream();
try {
  await data.pipeThrough(limitBytes(1024)).pipeTo(new WritableStream());
} catch (err) {
  console.log(err.name, '-', err.message);
}
RangeError - Limit von 1024 Bytes überschritten

Zeitüberschreitung über gespeicherten Controller

const withTimeout = (ms) => {
  let timer;
  return new TransformStream({
    start(controller) {
      // error() funktioniert auch außerhalb der Callbacks
      timer = setTimeout(() => controller.error(new Error('Timeout')), ms);
    },
    transform(chunk, controller) {
      controller.enqueue(chunk);
    },
    flush() {
      clearTimeout(timer);
    }
  });
};

const { readable, writable } = withTimeout(100);
const writer = writable.getWriter();
writer.write('a');

const reader = readable.getReader();
console.log(await reader.read());
try {
  await reader.read(); // wartet, bis der Timeout greift
} catch (err) {
  console.log('Fehler:', err.message);
}
{ value: 'a', done: false } Fehler: Timeout

// Wichtig · Fallstricke

  • Unumkehrbar: Ein Stream im Fehlerzustand lässt sich nicht zurücksetzen. Für neue Daten braucht man einen neuen TransformStream.
  • Chunks gehen verloren: Chunks, die schon eingereiht, aber noch nicht gelesen wurden, werden verworfen. Leser sehen nur noch den Fehler.
  • Nach error() nicht mehr enqueue() aufrufen: Ein späteres controller.enqueue() wirft einen TypeError. Deshalb im Beispiel sofort return.
  • Mehrfachaufruf: Weitere Aufrufe von error() nach dem ersten bewirken nichts. Der erste Fehlergrund bleibt bestehen.
  • Abgrenzung: terminate() schließt die lesbare Seite normal und lässt nur die schreibbare Seite fehlschlagen. error() lässt dagegen beide Seiten fehlschlagen.
  • Unbehandelte Ablehnungen: pipeTo(), write() und read() lehnen ab. Diese Promises immer mit try/catch oder .catch() behandeln, sonst erscheinen Warnungen über unhandled rejections.
  • Kompatibilität: Wird von Chrome/Edge 67+, Firefox 102+ und Safari 14.1+ unterstützt. In Node.js steht die Methode ab Version 18 global zur Verfügung, in älteren Versionen über stream/web.