Troubleshooting & FAQ¶
Use this checklist when something feels off. Most responses link back to other chapters for deeper fixes.
Web component isn’t defined¶
Symptom: The console shows customElements.define… already used or sciflow-editor is not defined.
Fixes:
- Import the bundle only once per page. When using bundlers, prefer a single entry point that re-exports
@sciflow/editor-start. - If you hot-reload modules, guard the definition:
Duplicate definitions
Loading the bundle twice on the same page throws NotSupportedError. Ensure that both the demo bundle and your application bundle aren’t included together.
Toolbar icons are blank squares¶
As of the current release, toolbar icons are bundled as inline SVGs and no external font is required. If you see blank squares, you may be using an older version of @sciflow/editor-start that relied on the Google Fonts CDN. Upgrade to the latest version, which includes all icons in the bundle. See Icons for details.
Paste turns everything into citations¶
The citation feature only converts clipboard payloads marked with application/x-sciflow-reference. If you see unexpected conversions:
- Confirm that your clipboard handler isn’t injecting JSON with
{ type: 'reference' }. - Update to the latest bundle where paste detection ignores plain text that isn’t tagged as a reference (fix merged via
extractDropPayloadhardening).
Outline clicks don’t scroll the editor¶
Call commands.focus() before commands.scrollIntoView() (already built into the custom outline recipe). Focus ensures that browsers actually honor the scroll request.
Equations show as placeholder or raw TeX¶
Symptom: Math nodes display a placeholder message or raw TeX instead of rendered SVG.
Fix: The math feature is enabled by default, but the host page must load MathJax 4 for equations to render. Add this script tag to your HTML (typically in <head>):
Without it, equations still save and load correctly; only the visual rendering is deferred.
Registry returns 404¶
@sciflow/editor-start, @sciflow/editor-core, @sciflow/schema-prosemirror,
@sciflow/schema-core, @sciflow/pandoc-ast and @sciflow/pandoc-web resolve on the
public npm registry and need no registry configuration. If one of them 404s, check that
no .npmrc in the project, your home directory, or NPM_CONFIG_USERCONFIG is
redirecting the @sciflow scope elsewhere.
@sciflow/reader is not on the public registry, so a 404 for that one is expected
rather than a misconfiguration. See Getting Started.
The first import is slow and downloads ~58 MB¶
Symptom: The first DOCX / Markdown / LaTeX import takes far longer than later ones, and the network panel shows a large pandoc.wasm request.
Cause: This is by design. @sciflow/pandoc-web runs Pandoc in the browser, and the Pandoc binary is ~58 MB uncompressed. It is fetched lazily on the first conversion — never on page load — and then reused for the rest of the page's lifetime.
Fixes:
- Show a loading state.
<sciflow-pandoc-drop>renders one by default and emitssciflow-status; if you driveconvertFile()yourself, put up your own indicator before awaiting it. - Compress the asset. With gzip the transfer is ~16 MB, with brotli ~11 MB. Make sure your server or CDN compresses
application/wasm; many default configurations do not. - Cache it properly. Vite, Webpack and Rspack emit
pandoc.wasmwith a content hash, so it is safe to serve with a longCache-Control: max-age. Without one, every page load pays the download again. - Warm it early if you can. When you know an import is coming (the user opened an import screen), call
loadPandoc()and ignore the result. The instance is memoized, so the real conversion reuses it.
Later conversions on the same page cost nothing extra — only the first one pays.
See Importing documents for the full size and caching notes.
WASM fetch fails with 404 after bundling¶
Symptom: Import works in development but breaks in a production build. The console shows a 404 for pandoc.wasm, or a WASM error such as CompileError: … magic word / expected magic word 00 61 73 6d — that second form is the WASM compiler being handed the HTML body of a 404 page.
Cause: The binary was not emitted next to your bundle. @sciflow/pandoc-web depends on pandoc-wasm like any other package and imports it through its package entry point, which asks the bundler for the URL of pandoc.wasm — but only a bundler configured to treat .wasm as an asset to copy, rather than as a module to parse or a dependency to pre-bundle, will hand back a URL that resolves.
Fixes:
- Confirm the file exists in your build output (
dist/assets/pandoc-*.wasmor equivalent) and that requesting its URL returnsContent-Type: application/wasm, not HTML. - Vite: emit the binary from a
preplugin, keep.wasminassetsIncludealongside it, and keep the dependency out of pre-bundling, which rewrites the asset URL out from under the engine — the full config is in Bundling. A build that stops on an unresolvedwasi_snapshot_preview1rather than 404ing is that plugin missing: Vite is compiling the binary as a module instead of copying it. - Webpack 5 / Rspack: if you have a custom
module.rulesentry matching.wasm, make sure this file falls undertype: 'asset/resource'. A WASM loader that tries to instantiate it will fail on its WASI imports (wasi_snapshot_preview1). - Check that the
pandoc-wasmdependency actually installed its.wasmbinary. It is a ~58 MB file, and installs that skip large artifacts (an aggressive or partial CI cache) can leave it missing while everything else resolves. - If you serve the bundle from a CDN on a different origin, the WASM request follows the same CORS rules as any other asset — see CORS errors above.
Because loadPandoc() memoizes its promise, a failed load stays failed for the life of the page: reload after fixing the deployment.
Content security policy blocks styles or scripts¶
Symptom: The editor renders but looks unstyled, or the console shows CSP violations like Refused to apply inline style.
Fixes:
-
style-src 'unsafe-inline'is required. The editor's own component styles use constructed stylesheets, but two paths create ordinary inline<style>elements, and no API exists to put a nonce on either of them:- Editor content styles are injected once into
document.headas<style data-sf-content-styles>when the first editor instance connects (the editable surface lives in the light DOM, which a shadow-root stylesheet cannot reach). - Consumer overrides passed to
shadowStyles/setShadowStyles()are appended as<style>elements inside the shadow root.
Both are blocked by a
style-srcthat lists only origins or a nonce, and the symptom is exactly the unstyled editor above. There is no nonce-only configuration for this release. 2. If you load MathJax from a CDN, addcdn.jsdelivr.nettoscript-src. 3. For WebSocket sync, add your sync server toconnect-src. - Editor content styles are injected once into
See Browser Compatibility for the full directive list.
CORS errors when loading images or syncing¶
Symptom: Images don't load, or the sync connection fails with Access-Control-Allow-Origin errors.
Fixes:
- Configure your image server or CDN to return
Access-Control-Allow-Originfor your application's origin. - For WebSocket connections (Yjs sync), the WebSocket server must accept connections from your origin.
- If serving the editor bundle from a different domain, ensure CORS headers are set on that domain too.
Tables look broken or can't be edited¶
Symptom: Table cells aren't selectable, columns can't be resized, or table markup appears instead of a rendered table.
Fixes:
- Ensure the table feature is included in your feature set. If you use a custom
featuresarray, add the table feature explicitly. - Table CSS is part of
sciflow-editor.css. Verify the stylesheet is loaded. - Column resizing requires pointer events — if you have
pointer-events: noneon a parent container, it will break table interactions.
ProseMirror devtools integration¶
For debugging document state, install the ProseMirror DevTools browser extension. Access the ProseMirror view for debugging:
const editor = document.getElementById('editor');
// Access the underlying ProseMirror EditorView
const view = editor.editorView;
console.log('Current doc:', view.state.doc.toJSON());
console.log('Selection:', view.state.selection.toJSON());
Debug logging¶
SciFlow logs errors to the console prefixed with [sciflow-editor]:
| Message | Cause |
|---|---|
failed to initialize editor |
Initialization failure — check for duplicate custom element definitions |
dropping invalid link node |
A cross-reference node has a malformed href or missing target |
unable to read supplied CSSStyleSheet |
Shadow style injection failed — check that your CSS is valid |
received invalid doc JSON; falling back to default document |
The doc property was set to invalid JSON |
To inspect plugin state programmatically:
import { PluginKey } from 'prosemirror-state';
const key = new PluginKey('my-plugin');
const state = editor.plugins.getState(key);
console.log('Plugin state:', state);
Collaborative cursors not showing¶
Symptom: Multiple users are editing but remote cursors or selections are invisible.
Fixes:
- Ensure you're using the Yjs sync adapter with awareness enabled on your
YjsProvider. - Remote cursor styles require the editor's default CSS. If you override Shadow DOM styles, verify the cursor classes (
.yjs-cursor,.yjs-selection) aren't hidden. - Check that the WebSocket connection is active — cursors rely on Yjs Awareness, which needs a live connection.
Schema validation errors on load¶
Symptom: The editor loads a default empty document instead of your content, with a console error about invalid JSON.
Fixes:
- Validate your document JSON against the generated schema:
Then compare your JSON against
manuscript-snapshot.schema.json. - Common causes: missing required
typefields on nodes, invalid attribute values, content that doesn't match the node's content expression. - After schema updates, existing documents may need migration. See Content Import & Export.
Need help?¶
- Review the User Guide sections again.
- Check the Developer Guide if you suspect a build issue.
- See Browser Compatibility for environment-specific issues.
- Still stuck? Capture logs/screenshots and share them with the core team.