Prettier 3.0 converted the Node API to promises. prettier.format(source, options) returns a Promise rather than a string, and the same applies to formatWithCursor, resolveConfig, getFileInfo, and check. Code written for Prettier 2 keeps compiling and silently produces [object Promise] where formatted text was expected, or writes that string into a file.
The fix is to await every call and to make the enclosing function async, which propagates through build scripts, code generators, and ESLint or lint-staged integrations that formatted output inline.
The change accompanied Prettier 3 shipping as ESM internally. A CommonJS caller can still require('prettier'), but plugins are loaded asynchronously, which is the underlying reason the API had to change. If a tool in the chain cannot be made async, pinning to Prettier 2 is the only alternative, and Prettier 2 no longer receives fixes, so treat it as temporary.