Skip to content

[Tokenizer / MultiInput / Input]: throw "Web component processed too many times this task" during sustained ResizeObserver pressure (FCL separator drag, window resize across the desktop/tablet breakpoint) #8668

@JackieWei

Description

@JackieWei

Describe the bug

Under sustained width-change pressure, UI5 throws

Uncaught Error: Web component processed too many times this task, max allowed is: 10
    at o.process (RenderQueue.js:47)

The element on the call stack is one of ui5-multi-input, ui5-tokenizer, or ui5-input. After the throw, RenderQueue is in a poisoned state: subsequent reactive writes never flush, click events on UI5 elements stop firing, and the page must be reloaded.

Isolated Example

No response

Reproduction steps

Most reliably with FlexibleColumnLayout + a filter row containing a MultiInput whose tokenizer holds at least 2 tokens, but the underlying defect is independent of FCL — it is a same-task ResizeObserver feedback loop. Two reproductions:

Repro A — FCL separator drag

  1. Render a FlexibleColumnLayout in TwoColumnsMidExpanded.
  2. Place a MultiInput (or ValueHelpInput from libraries, which wraps MultiInput) inside the start column. Pre-populate it with ≥ 2 tokens.
  3. Drag the column separator quickly back and forth.

Stack trace:

RenderQueue.process @ RenderQueue.js:47
Render.js:51 (rAF callback)
UI5Element._invalidate
UI5Element setter (_inputWidth) ← Input.js:776 _handleResize
ResizeHandler tick

Repro B — Window resize at the desktop/tablet breakpoint

  1. Same setup as A.
  2. Resize the browser window back and forth across the ~1000 px boundary repeatedly.

Same error and same call stack.

Expected Behaviour

Should able to handle resize frequently at any level

Screenshots or Videos

No response

UI5 Web Components for React Version

~2.16.0

UI5 Web Components Version

~2.16.0

Browser

Chrome

Operating System

MacOS

Additional Context

Root cause (analysis)

The pattern is the same in Input._handleResize (line 776 of Input.js) and Tokenizer._handleResize (line 78 of Tokenizer.js):

// Input.js
_handleResize() {
  this._inputWidth = this.offsetWidth;
}
// Tokenizer.js
_handleResize() {
  this._nMoreCount = this.overflownTokens.length;
}

Each callback writes a width-derived value back into a reactive property. The reactive write triggers _invalidate, which queues the same component into RenderQueue. Within a single animation frame, ResizeObserver can deliver multiple entries for the same element; combined with cascaded width changes from sibling elements that each queue further invalidations, the same component is processed > 10 times in one task. RenderQueue's MAX_PROCESS_COUNT = 10 limit then trips.

Specifically:

  1. ResizeObserver tick → _handleResize writes reactive prop.
  2. The reactive write triggers _invalidate → component is queued for the next render pass.
  3. Next render lays out the page, which can change width of sibling components (FCL animation, MultiInput overflow recalculation propagates token list width to the hosting Input).
  4. Sibling ResizeObservers fire in the same task → goto 1.
  5. After 10 passes RenderQueue throws.

The 10-pass ceiling is correct as a runaway guard, but the components themselves do not protect against feeding their own ResizeObserver. The _handleResize body should:

  • Skip the write if the observed dimension has not changed since the last commit, or
  • RAF-coalesce so multiple ticks within one animation frame collapse to one write.

A workaround we deployed at the application layer:

// Patch _handleResize on every ui5-multi-input / ui5-tokenizer / ui5-input
// inside our filter row so the reactive write is RAF-throttled and
// suppressed when width is unchanged.
function patchOne(el) {
  const original = el._handleResize;
  let pending = null;
  let lastWidth;
  el._handleResize = function () {
    const width = this.offsetWidth;
    if (width === lastWidth) return;
    if (pending !== null) return;
    pending = requestAnimationFrame(() => {
      pending = null;
      const next = this.offsetWidth;
      if (next === lastWidth) return;
      lastWidth = next;
      original.call(this);
    });
  };
}

This eliminates the loop without changing behaviour. The patch is a hack — fixing it inside UI5 itself is the right place.

Suggested fix

Inside Input._handleResize and Tokenizer._handleResize:

_handleResize() {
  const w = this.offsetWidth;
  if (w === this._lastObservedWidth) return;
  this._lastObservedWidth = w;
  this._inputWidth = w; // or _nMoreCount = this.overflownTokens.length;
}

Even better, RAF-coalesce so successive ticks within one frame produce at most one reactive write.

Why this matters

Any application that places MultiInput (directly or through ValueHelpInput) in a FlexibleColumnLayout start column is vulnerable. The bug is data-dependent (token count) and timing-dependent (sustained resize), so it does not show up in unit tests but is reliably hit in production by users dragging the separator or resizing the window. Once thrown, the entire page must be reloaded.

Relevant log output

Organization

No response

Declaration

  • I’m not disclosing any internal or sensitive information.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions