OrchardCMS/OrchardCore · error · TypeError

Thenable self-resolution

Error message

Thenable self-resolution

What it means

This TypeError is thrown by jQuery's Promise implementation when a promise resolution handler returns the deferred's own promise — i.e. a promise is resolved with itself. The Promises/A+ spec (section 2.3.1) forbids self-resolution because it creates an unresolvable cycle, so jQuery deliberately fails the deferred with this TypeError instead of hanging forever.

Solutions

  1. Inspect the .then/.pipe handler on the stack: change it to return a NEW promise/deferred, not the same deferred.promise() it is chained on.
  2. If resolving a deferred with another promise, verify the value !== the deferred's own promise before resolve().
  3. Refactor to return the value (or $.Deferred-based promise created inside the handler) rather than the enclosing deferred.
  4. Where cycles are possible, guard: if (candidate === myPromise) { throw new TypeError('self-resolution'); } before resolving.

Example fix

// before
function run() {
  const d = $.Deferred();
  d.then(() => d.promise()); // TypeError: Thenable self-resolution
  return d.promise();
}
// after
function run() {
  return $.Deferred(async (d) => {
    const result = await step();
    d.resolve(result); // resolve with a value or a DIFFERENT promise
  }).promise();
}
Defensive patterns

Strategy: type-guard

Validate before calling

function safeResolve(d, value) {
  if (value === d.promise()) throw new TypeError('Cannot resolve deferred with its own promise');
  d.resolve(value);
}

Type guard

function isSelfResolution(d, value) { return value != null && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function' && value === d.promise(); }

Try / catch

promise.then(handler).catch(function (e) { if (e instanceof TypeError && e.message === 'Thenable self-resolution') { return rebuildChain(); } throw e; });

Prevention

When it happens

Trigger: Inside a .then()/.pipe() handler (or Deferred resolution chain), returning the same deferred.promise() object that is being resolved — e.g. `return d.promise()` from a handler attached to `d.promise()` itself, or resolving a deferred with its own promise.

Common situations: Chaining retries where a handler mistakenly returns the original deferred instead of a new one; recursive wrapper functions that accidentally return the outer promise; converting callback code to promises and reusing a single module-level deferred; mistakenly returning `arguments.callee`-style same-promise from .then.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/13b31a871a7e9314. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore.Modules/OrchardCore.Resources/wwwroot/Vendor/jquery-3.5.1/jquery.js:3767

						return function() {
							var that = this,
								args = arguments,
								mightThrow = function() {
									var returned, then;

									// Support: Promises/A+ section 2.3.3.3.3
									// https://promisesaplus.com/#point-59
									// Ignore double-resolution attempts
									if ( depth < maxDepth ) {
										return;
									}

									returned = handler.apply( that, args );

									// Support: Promises/A+ section 2.3.1
									// https://promisesaplus.com/#point-48
									if ( returned === deferred.promise() ) {
										throw new TypeError( "Thenable self-resolution" );
									}

									// Support: Promises/A+ sections 2.3.3.1, 3.5
									// https://promisesaplus.com/#point-54
									// https://promisesaplus.com/#point-75
									// Retrieve `then` only once
									then = returned &&

										// Support: Promises/A+ section 2.3.4
										// https://promisesaplus.com/#point-64
										// Only check objects and functions for thenability
										( typeof returned === "object" ||
											typeof returned === "function" ) &&
										returned.then;

									// Handle a returned thenable
									if ( isFunction( then ) ) {

View on GitHub (pinned to 4306c0717f)