siyuan-note/siyuan · error

'promise.then property is not a function

Error message

'promise.then property is not a function

What it means

invokeFunction confirmed the result is a Promise and ToObject succeeded, but obj.Get("then") is not callable via goja.AssertFunction — the object lacks a real .then method. This means a thenable-shaped object whose then is missing or not a function.

Source

Thrown at kernel/plugin/sandbox.go:394

		callback(rt, &CallResult{Error: invokeErr})
		return
	}

	result := resultJs.Export()
	if isGoPromise(result) {
		if !async {
			panic(fmt.Errorf("synchronous function returned a Promise"))
		}
		resultObj := resultJs.ToObject(rt)
		if resultObj == nil {
			callback(rt, &CallResult{Error: fmt.Errorf("expected promise object, got %T", result)})
			return
		}

		thenValue := resultObj.Get("then")
		then, ok := goja.AssertFunction(thenValue)
		if !ok {
			callback(rt, &CallResult{Error: fmt.Errorf("'promise.then property is not a function")})
			return
		}

		then(resultObj, rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) {
			// ⚠️ call.Arguments always is an empty array.
			promise, ok := result.(*goja.Promise)
			if ok {
				callback(rt, &CallResult{Value: promise.Result()})
			} else {
				callback(rt, &CallResult{Value: resultJs})
			}
		}), rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) {
			callback(rt, &CallResult{Error: fmt.Errorf("promise rejected: %v", call.Argument(0).Export())})
		}))
	} else {
		callback(rt, &CallResult{Value: resultJs})
	}
}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Return a genuine native Promise from the handler rather than a hand-built thenable.
  2. Verify Promise.prototype.then is intact in the sandbox (no monkey-patching that removes it).
  3. If returning a thenable, ensure it exposes a callable .then(onFulfilled, onRejected).

Example fix

// before
handler = () => ({ then: null })
// after
handler = () => new globalThis.Promise((resolve) => resolve(1))
Defensive patterns

Strategy: type-guard

Validate before calling

// Plugin side: verify .then is a function before returning a thenable.
function safeThenable(v: any): v is Promise<any> {
  return v != null && typeof (v as any).then === 'function'
}

Type guard

function hasCallableThen(v: unknown): v is { then: (...a: any[]) => any } {
  return !!v && typeof (v as any)?.then === 'function'
}

Prevention

When it happens

Trigger: A plugin returns an object that isGoPromise matched (e.g. has promise-like type) but whose .then was deleted, is undefined, or is a non-function property; a custom thenable with then: null; tampered Promise.prototype.then.

Common situations: Plugin overrides/deletes Promise.prototype.then; plugin returns a duck-typed thenable missing then; sandbox strips the then property for security.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/84f30f9707abab92. Report an issue: GitHub.