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 modelView on GitHub (pinned to d44925c47c)
Solutions
- Add the original component to its container before ever calling replaceWith on it.
- If unattached, just add the new component to the container instead of replaceWith.
- Guard with `if (old.getParent() == null) { container.add(replacement); } else { old.replaceWith(replacement); }`.
- Ensure replaceWith is invoked during the request/render phase, not teardown.
- 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
- Only replaceWith components created and added during the same request
- Never call replaceWith during onDetach/detach phases
- For fresh components, add to the container directly
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
- Cannot remove
- Cannot determine Markup. Component is not yet connected to a
- has not been properly initialized. Something in the hierarc
- has not been properly added. Something in the hierarchy of
- has not been properly detached. Something in the hierarchy
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/99881eee691aa7a9.
Report an issue: GitHub.