pandas-dev/pandas · error · ValueError
Cannot return a copy of the target
Error message
Cannot return a copy of the target
What it means
When inplace=False and there is an assignment, eval.py:417 copies the target on the first assignment so the original object is untouched. For NDFrame it uses copy(deep=False); for anything else it calls target.copy(). If the target object has no copy method, AttributeError is caught at eval.py:424 and re-raised as this ValueError naming the conceptual problem (cannot return a copy).
Source
Thrown at pandas/core/computation/eval.py:425
)
if inplace:
raise ValueError("Cannot operate inplace if there is no assignment")
# assign if needed
assigner = parsed_expr.assigner
if env.target is not None and assigner is not None:
target_modified = True
# if returning a copy, copy only on the first assignment
if not inplace and first_expr:
try:
target = env.target
if isinstance(target, NDFrame):
target = target.copy(deep=False)
else:
target = target.copy()
except AttributeError as err:
raise ValueError("Cannot return a copy of the target") from err
else:
target = env.target
# TypeError is most commonly raised (e.g. int, list), but you
# get IndexError if you try to do this assignment on np.ndarray.
# we will ignore numpy warnings here; e.g. if trying
# to use a non-numeric indexer
try:
if inplace and isinstance(target, NDFrame):
target.loc[:, assigner] = ret
else:
target[assigner] = ret # pyright: ignore[reportIndexIssue]
except (TypeError, IndexError) as err:
raise ValueError("Cannot assign expression output to target") from err
if not resolvers:
resolvers = ({assigner: ret},)
else:View on GitHub (pinned to 71959b8cb9)
Solutions
- Set inplace=True to skip the copy path entirely.
- Pass an NDFrame (DataFrame/Series) target, which has a working copy(deep=False).
- Add a .copy() method to your custom target class that returns a shallow copy.
Example fix
// before
pd.eval('a = 1', target=my_obj, inplace=False)
// after
pd.eval('a = 1', target=my_obj, inplace=True) Defensive patterns
Strategy: validation
Validate before calling
def validate_target_supports_copy(target) -> None:
if not hasattr(target, 'copy'):
raise ValueError(
f'target {type(target).__name__} has no .copy(); '
'use inplace=True or an NDFrame/dict target'
)
# only when inplace=False and an assignment is present:
if not inplace and has_assignment:
validate_target_supports_copy(target) Type guard
def target_can_copy(target) -> bool:
return hasattr(target, 'copy') and callable(getattr(target, 'copy')) Try / catch
try:
pd.eval(expr, target=target, inplace=False)
except ValueError as e:
if 'copy of the target' in str(e):
pd.eval(expr, target=target, inplace=True) # mutate instead
else:
raise Prevention
- Prefer DataFrame/dict targets which support copy.
- Use inplace=True for custom targets that lack copy.
- Add a .copy() method to custom namespace objects used as eval targets.
When it happens
Trigger: pd.eval('a = 1', target=some_object, inplace=False) where some_object lacks a .copy() method — e.g. a custom dict subclass, a list, or a third-party container.
Common situations: Plugging a custom namespace object as target. Using a plain dict-like that doesn't implement copy. Testing eval with a mock target.
Related errors
- multi-line expressions are only valid in the context of data
- Cannot operate inplace if there is no assignment
- Cannot assign expression output to target
- cannot assign without a target object
- The '@' prefix is only supported by the pandas parser
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/709c7baff902ec36.
Report an issue: GitHub.