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

  1. Set inplace=True to skip the copy path entirely.
  2. Pass an NDFrame (DataFrame/Series) target, which has a working copy(deep=False).
  3. 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

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


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/709c7baff902ec36. Report an issue: GitHub.