Skip to content

Editor.exportDocx() swallows export errors — catch returns undefined instead of rethrowing #3579

@jacobjove

Description

@jacobjove

What happens

Editor.exportDocx() wraps its whole body in a try { … } catch. The catch logs and emits, but has no return or throw, so on any export error the method resolves to undefined and the real error only reaches stderr:

https://github.com/superdoc-dev/superdoc/blob/main/packages/super-editor/src/editors/v1/core/Editor.ts#L3916-L3920

      return result;
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.emit('exception', { error: err, editor: this });
      console.error(err);
      // ← no return / no rethrow: the function resolves to `undefined`
    }
  }

Why it's a problem

A caller that does const bytes = await editor.exportDocx() gets undefined with no thrown error to catch. The downstream failure surfaces far from the cause — e.g. … is not binary (got undefined) when the bytes are later written — and the actual TypeError is buried in console.error. Anyone hitting an export-path failure has to reverse-engineer it from stderr instead of catching a rejected promise.

How it was hit

Building a headless/collab editor with no docx source: converter.convertedXml lacks the base OOXML parts, and exportDocx's header/footer export dereferences them, throwing a TypeError. The catch swallowed it to undefined. (Context: #3568 / #3569, where seeding a blank-docx template avoids the throw — but that's a workaround for that path, not for the swallowing itself, which affects every export error.)

Suggested fix

Rethrow (or return/reject) from the catch after emitting, so the error propagates to the awaiting caller:

    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.emit('exception', { error: err, editor: this });
      throw err; // propagate instead of resolving undefined
    }

Keeping the exception emit preserves existing observers; the change is that callers can now try/catch (or .catch) a real rejection. Same pattern appears in the sibling catch a few lines down (~L3932) and could get the same treatment. Worth checking existing consumers that may rely on the current undefined-on-error behavior before changing the contract.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    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