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

  1. Verify the captured content is non-empty before calling sendContentToJoplin (guard at the call site).
  2. Inspect the page-extraction step (the code that builds `content`) for empty returns on the target page.
  3. Disable and re-enable the clipper extension to reset popup state if a stale empty payload is being sent.
  4. 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

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.