laurent22/joplin · error · Error
Cannot send empty content
Error message
Cannot send empty content
What it means
Thrown by the web-clipper popup's sendContentToJoplin when the content argument is falsy (empty string, null, undefined). The bridge refuses to POST an empty body to the Joplin API because it would create an empty or malformed note.
Source
Thrown at packages/app-clipper/popup/src/bridge.js:389
}
const response = await fetch(`${baseUrl}/${path}${queryString}`, fetchOptions);
if (!response.ok) {
const msg = await response.text();
throw new Error(msg);
}
const json = await response.json();
return json;
}
async sendContentToJoplin(content) {
console.info('Popup: Sending to Joplin...');
try {
this.dispatch({ type: 'CONTENT_UPLOAD', operation: { uploading: true } });
if (!content) throw new Error('Cannot send empty content');
// There is a bug in Chrome that somehow makes the app send the same request twice, which
// results in Joplin having the same note twice. There's a 2-3 sec delay between
// each request. The bug only happens the first time the extension popup is open and the
// Complete button is clicked.
//
// It's beyond my understanding how it's happening. I don't know how this sendContentToJoplin function
// can be called twice. But even if it is, logically, it's impossible that this
// call below would be done with twice the same nounce. Even if the function sendContentToJoplin
// is called twice in parallel, the increment is atomic and should result in two nounces
// being generated. But it's not. Somehow the function below is called twice with the exact same nounce.
//
// It's also not something internal to Chrome that repeat the request since the error is caught
// so it really seems like a double function call.
//
// So this is why below, when we get the duplicate nounce error, we just ignore it so as not to display
// a useless error message. The whole nounce feature is not for security (it's not to prevent replay
// attacks), but simply to detect these double-requests and ignore them on Joplin side.View on GitHub (pinned to 2654b33620)
Solutions
- Verify the captured content is non-empty before calling sendContentToJoplin (guard at the call site).
- Inspect the page-extraction step (the code that builds `content`) for empty returns on the target page.
- Disable and re-enable the clipper extension to reset popup state if a stale empty payload is being sent.
- Update the clipper extension to the latest version — the surrounding code references a known double-send Chrome bug.
Example fix
// before
if (!content) throw new Error('Cannot send empty content');
// after — guard the caller and explain why
if (!content || !content.trim()) {
throw new Error('Cannot send empty content — no text was captured from the page.');
} Defensive patterns
Strategy: validation
Validate before calling
if (!content || !content.trim()) {
console.warn('No content captured; not sending.');
return;
}
await bridge.sendContentToJoplin(content); Type guard
function hasSendableContent(content: unknown): content is string {
return typeof content === 'string' && content.trim().length > 0;
} Try / catch
try {
await bridge.sendContentToJoplin(content);
} catch (e) {
if (e.message === 'Cannot send empty content') {
// show the user that nothing was captured; do not retry
} else throw e;
} Prevention
- Guard sendContentToJoplin callers with a non-empty check including .trim().
- Verify page content extraction returned a value before enabling the send button.
- Re-load the clipper extension if the popup holds stale empty state.
When it happens
Trigger: Calling sendContentToJoplin(content) with an empty string or null/undefined. This can happen when the clipper captures no selected text, the page body extraction returns nothing, or the user clicks the send button before content is populated.
Common situations: Clipping a page that has no extractable body (e.g. a pure-canvas app); the content extraction ran before the DOM was ready; a browser extension race where the popup opens before the content script returns data; double-invocation bug noted in the surrounding comments.
Related errors
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/a5d9153229f3f2b4.
Report an issue: GitHub.