# Skip diff if before is empty

**URL:** <https://forum.ansible.com/t/skip-diff-if-before-is-empty/45954>\
**Category:** Get Help\
**Tags:** ansible-core\
**Created:** [June 21, 2026, 8:53am UTC](https://forum.ansible.com/t/skip-diff-if-before-is-empty/45954 "2026-06-21T08:53:08Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![etfz](https://avatars.discourse-cdn.com/v4/letter/e/e95f7d/32.png) [@etfz](https://forum.ansible.com/u/etfz)\
**Post date:** [June 21, 2026, 8:53am UTC](https://forum.ansible.com/t/skip-diff-if-before-is-empty/45954/1 "2026-06-21T08:53:08Z")

</div>

Hi,

When using the `copy` module, I would like to omit the diff output if the destination file does not exist. It seems like the pieces are all there, but they don’t quite fit together.

I wanted to do something like this:

```auto
diff: results.diff.before != ''

```

but I think that results are not available at this point? What’s the easiest way to accomplish this? Prepared to create a custom module if necessary for keeping the playbook simple.

---

<div class="post-metadata">

**Author:** ![kks](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/kks/32/10352_2.png) [@kks](https://forum.ansible.com/u/kks)\
**Post date:** [June 21, 2026, 9:15am UTC](https://forum.ansible.com/t/skip-diff-if-before-is-empty/45954/2 "2026-06-21T09:15:24Z")

</div>

> [@etfz](#):
>
> `diff: results.diff.before != ''`

`diff: results.diff.before | default('') != ''`

See [Using filters to manipulate data — Ansible Community Documentation](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_filters.html#providing-default-values)

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [June 21, 2026, 9:32am UTC](https://forum.ansible.com/t/skip-diff-if-before-is-empty/45954/3 "2026-06-21T09:32:35Z")

</div>

The default doesn’t help a bit, since the value for `diff` for the current task is determined _before_ the task is executed, and therefore in it you cannot use data from the _result_ of the current task.

Basically the only way this is possible (with a single task) is through a custom callback plugin, which handles copy tasks specially.

Or you could add a `ansible.builtin.stat` task first that determines whether the file exists, and set `diff` accordingly (obviously this has a race condition, assuming someone else creates the file between the `ansible.builtin.stat` task and the `ansible.builtin.copy` task).

---

<div class="post-metadata">

**Author:** ![etfz](https://avatars.discourse-cdn.com/v4/letter/e/e95f7d/32.png) [@etfz](https://forum.ansible.com/u/etfz)\
**Post date:** [June 22, 2026, 6:50am UTC](https://forum.ansible.com/t/skip-diff-if-before-is-empty/45954/4 "2026-06-22T06:50:15Z")

</div>

I see. Thank you.

A custom callback plugin would be preferable, in that case. Are there any examples of targeting specific modules?

---

<div class="post-metadata">

**Author:** ![shertel](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/shertel/32/1241_2.png) [@shertel](https://forum.ansible.com/u/shertel)\
**Post date:** [June 22, 2026, 2:31pm UTC](https://forum.ansible.com/t/skip-diff-if-before-is-empty/45954/5 "2026-06-22T14:31:38Z")

</div>

> **Here's an example:**
>
> ```python
> from __future__ import annotations
> 
> DOCUMENTATION = """
> name: copy_diff
> type: stdout
> short_description: Custom diff callback.
> description:
> - The default output callback for ansible-playbook with modified diff output for the copy module.
> extends_documentation_fragment:
> - default_callback
> - result_format_callback
> requirements:
> - set as stdout in configuration
> """
> 
> import os
> 
> from ansible.plugins.loader import callback_loader
> 
> CallbackBase = callback_loader.get("ansible.builtin.default", class_only=True)
> 
> class CallbackModule(CallbackBase):
> CALLBACK_VERSION = 2.0
> CALLBACK_TYPE = 'stdout'
> CALLBACK_NAME = 'copy_diff'
> 
> def v2_on_file_diff(self, result: CallbackTaskResult) -> None:
> if result._task.resolved_action == "ansible.builtin.copy" and not os.path.exists(result.task.args["dest"]):
> return
> 
> return super(CallbackModule, self).v2_on_file_diff(result)
> 
> ```

Note that other actions, like `assemble` and `template` use the `copy` module, and wouldn’t be handled by this example.

---

<div class="post-metadata">

**Author:** ![U880D](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/u880d/32/10032_2.png) [@U880D](https://forum.ansible.com/u/U880D)\
**Post date:** [June 24, 2026, 8:43am UTC](https://forum.ansible.com/t/skip-diff-if-before-is-empty/45954/6 "2026-06-24T08:43:36Z")

</div>

> [@felixfontein](#):
>
> Or you could add a `ansible.builtin.stat` task first that determines whether the file exists, and set `diff` accordingly …

Just for the records

```yaml
---
- hosts: test
  become: false
  gather_facts: false

  pre_tasks:

    - name: Gathering File Facts
      ansible.builtin.stat:
        path: test.txt
      register: file_facts

  tasks:

    - name: Copy file to Remote Node
      ansible.builtin.copy:
        src: "test.txt"
        dest: "test.txt"
      diff: "{{ file_facts.stat.exists }}"

```

---

<div class="post-metadata">

**Author:** ![etfz](https://avatars.discourse-cdn.com/v4/letter/e/e95f7d/32.png) [@etfz](https://forum.ansible.com/u/etfz)\
**Post date:** [June 28, 2026, 8:41pm UTC](https://forum.ansible.com/t/skip-diff-if-before-is-empty/45954/7 "2026-06-28T20:41:15Z")

</div>

Thanks! I’ve used this for now, though I’m checking the `before` property instead, since `dest` can be a few different things, such as a destination directory that the file goes in.

One tangential thing I wanted to try doing with this is printing (or not) the diff for each file when `copy`ing a directory. Currently is only prints “changed” - without any details - when any file has changed. It doesn’t seem to be handled by `v2_on_file_diff`.

---

<div class="post-metadata">

**Author:** ![shertel](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/shertel/32/1241_2.png) [@shertel](https://forum.ansible.com/u/shertel)\
**Post date:** [June 29, 2026, 3:59pm UTC](https://forum.ansible.com/t/skip-diff-if-before-is-empty/45954/8 "2026-06-29T15:59:18Z")

</div>

> I’ve used this for now, though I’m checking the `before` property instead, since `dest` can be a few different things, such as a destination directory that the file goes in.

True, I overlooked that.

> One tangential thing I wanted to try doing with this is printing (or not) the diff for each file when `copy`ing a directory. Currently is only prints “changed” - without any details - when any file has changed. It doesn’t seem to be handled by `v2_on_file_diff`.

There are multiple issues with the current copy diff and the callback is not the right place to fix it, so the multi-task approach would be a lot more reliable. The copy action plugin returns a single file diff (related: [copy action plugin diff output for multi files 2 by simonLeary42 · Pull Request #84423 · ansible/ansible · GitHub](https://github.com/ansible/ansible/pull/84423)) only containing the changed content or the diff returned by the file module (so it can be incomplete and formatted differently depending on the before state), and the copy module does not return a diff at all (related: [[POC] copy - add diff mode support for remote\_src=true by s-hertel · Pull Request #84324 · ansible/ansible · GitHub](https://github.com/ansible/ansible/pull/84324)). I don’t think there’s a precedent in ansible-core of how to display the diff for multiple files, but I’ve been hesitant to add a 3rd format since it’s already inconsistent for a single file diff (and once something is added, it will be difficult to change). I think the task result diff object _should_ contain all changes and be a consistent format, but it could be worse visually for people who like the existing diff behavior of copy, and without defining a better diff API for callback plugins, there isn’t a good way to customize what is displayed.
