theonedev/onedev · error · IllegalStateException

This method can only be called on a component that has alrea

Error message

This method can only be called on a component that has already been added to its parent.

What it means

Component.replaceWith() requires the component to already have a parent, because the replacement is delegated to parent.replace(replacement). Calling it on an unattached component (parent == null) throws IllegalStateException — there is no parent in which to perform the replacement.

Source

Thrown at server-core/src/main/java/org/apache/wicket/Component.java:2829

	 * @since 1.2.1
	 * 
	 * @param replacement
	 *            component to replace this one
	 * @return the component which replaced this one
	 */
	public Component replaceWith(Component replacement)
	{
		Args.notNull(replacement, "replacement");

		if (!getId().equals(replacement.getId()))
		{
			throw new IllegalArgumentException(
				"Replacement component must have the same id as the component it will replace. Replacement id [[" +
					replacement.getId() + "]], replaced id [[" + getId() + "]].");
		}
		if (parent == null)
		{
			throw new IllegalStateException(
				"This method can only be called on a component that has already been added to its parent.");
		}
		parent.replace(replacement);
		return replacement;
	}

	/**
	 * @param component
	 *            The component to compare with
	 * @return True if the given component's model is the same as this component's model.
	 */
	public final boolean sameInnermostModel(final Component component)
	{
		return sameInnermostModel(component.getDefaultModel());
	}

	/**
	 * @param model

View on GitHub (pinned to d44925c47c)

Solutions

  1. Add the original component to its container before ever calling replaceWith on it.
  2. If unattached, just add the new component to the container instead of replaceWith.
  3. Guard with `if (old.getParent() == null) { container.add(replacement); } else { old.replaceWith(replacement); }`.
  4. Ensure replaceWith is invoked during the request/render phase, not teardown.
  5. Verify the component wasn't removed by earlier code in the handler.

Example fix

// before
Label fresh = new Label("msg", "hi");
fresh.replaceWith(new Label("msg", "bye")); // no parent yet
// after
container.add(new Label("msg", "hi"));
label.replaceWith(new Label("msg", "bye")); // label is attached
Defensive patterns

Strategy: type-guard

Validate before calling

if (old.getParent() == null) { container.add(replacement); return; }

Type guard

boolean replaceable(Component c) { return c.getParent() != null; }

Try / catch

try { old.replaceWith(replacement); } catch (IllegalStateException e) { log.warn("Not attached; adding directly", e); }

Prevention

When it happens

Trigger: Calling replaceWith() on a newly created component that was never added to a container; calling it after the component was removed; calling it on detached/serialized components whose parent link was dropped.

Common situations: Building a fresh component and calling replaceWith before adding; component removed earlier in the same request then replaced; calling replaceWith in onDetach after hierarchy teardown.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/99881eee691aa7a9. Report an issue: GitHub.