# Fallible 2025.12.5 released with Register Projections and new Action API previews

**URL:** <https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965>\
**Category:** News & Announcements\
**Created:** [December 6, 2025, 8:59am UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965 "2025-12-06T08:59:37Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![nitzmahone](https://avatars.discourse-cdn.com/v4/letter/n/c5a1d2/32.png) [@nitzmahone](https://forum.ansible.com/u/nitzmahone)\
**Post date:** [December 6, 2025, 8:59am UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965/1 "2025-12-06T08:59:37Z")

</div>

We’re happy to announce a new drop of the `fallible` experimental Ansible build, and are looking for your feedback! This one contains previews of a couple of features currently slated for release in Ansible Core 2.21:

- Register Projections allows the `register` keyword to set multiple vars using the result of arbitrary Jinja expressions. It adds a new implicit variable (`_task`) for the `register` keyword and task conditionals (e.g., `failed_when`, `changed_when`, `break_when`, `until`) to directly access the most recent task/loop results from Jinja expressions, as well as access to all previously-executed loop results (not just the most recent). This feature allows for more concise playbooks by collapsing the common “run a task, `set_fact` to stuff the interesting bits of the result into new vars” pattern into a single task.
- Action APIs for dynamic creation of hosts/groups and registration of top-level variables without `ansible_facts` (basically “write your own `add_host`, `group_by`, and `set_fact`”).

Huge props to @mattclay and @pkingstonxyz for the numerous hours spent with me solidifying the features to this point, to @sivel for bashing out the first couple iterations that we’d been handwaving about for so long, and to @mkrizek for the initial draft of the new Action API.

As with all `fallible` releases, this is completely unsupported, not a promise to ship anything in particular, might kick your dog, etc., but we’d love to hear your feedback on these features, since it’s still early enough to make changes. Some minimal examples and explanation can be found on the [PR](https://github.com/ansible/ansible/pull/86241) - a bit sparse at the moment, but hopefully enough to get the idea.

Get it from PyPI with `pip install fallible` if you want a side-by-side installation with existing Ansible Core (where the CLI tools are all `s/ansible/fallible/`, e.g. `fallible-playbook ...`), or `pip install fallible[compat]` if you want it to answer on the normal `ansible-*` CLI entrypoints.

Many of us are taking time off for the holidays, but feel free to discuss here or leave feedback on the [PR](https://github.com/ansible/ansible/pull/86241) if you’ve tried it and love it, hate it, it broke your world, or if you’ve got constructive feedback to share. We’ll follow up in January (if not before).

On behalf of the Ansible Core Team, thanks for being such an engaging community, and happy holidays!

---

<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:** [December 6, 2025, 2:04pm UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965/2 "2025-12-06T14:04:43Z")

</div>

To be honest I am not sure I like this additional option to register / set variables. I feel it is closer to an antipattern rather than a pattern. But of course it is not clear at the moment due to missing usage practice.  
Let me explain. Let’s consider examples from [Register projections and action plugin dynamic host/group/var API by nitzmahone · Pull Request #86241 · ansible/ansible · GitHub](https://github.com/ansible/ansible/pull/86241).

**Registering multiple variables from a task**

Before [1]:

```yaml
- shell: echo hello
  failed_when: echo_result.stdout is contains "goodbye"
  register: echo_result

- set_fact:
    echo_duration: "{{ echo_result.delta }}"
    capitalized_result: "{{ echo_result.stdout | capitalize }}"

```

After [2]:

```yaml
- shell: echo hello
  failed_when: _task.result.stdout is contains "goodbye"
  register:
    echo_result: _task.result # The old register behavior
    echo_duration: _task.result.delta
    capitalized_result: _task.result.stdout | capitalize

```

Putting “pure” functions like “{{ echo\_result.stdout | capitalize }}” and actions with side effects like “shell: echo hello” (one might argue about if this particular action has side effects, but this is just an simple example, in a broader case I assume actual action with side effect here) creates a problem for testing. In order to test that there are correct values in echo\_duration and capitalized\_result one has to run actions, because action and data manipulation are inseparable. Compare that for instance with this already existing approach of defining ansible role variables in vars/main.yml for for the role.

Using vars file [3]

```yaml
# vars/mail.yml file
echo_duration: _task.result.delta
capitalized_result: _task.result.stdout | capitalize

```

You do not have to have managed host to event be present - you can load var file, set \_task to what ever you want or expect and ensure that echo\_duration and capitalized\_result are set to expected values. So you can test this data transformation on each and every OS/ platform without even having this platform available. Imagine instead of `echo hello` there would be `cat /etc/os-release` and you want to transform data you got from this command to fit your needs. In listing [2] you have to have all the os versions available each and everytime you run tests, in listing [3] you only need data from `cat /etc/os-release` once so you can reuse it in any number of tests.  
Also by registering new variables (like echo\_duration and capitalized\_result) you are polluting variables in your playbook / role execution - because registered variables are available in the “global” scope. Vars file variables are available only in the role there they are defined.

Similar argument applies to example \*\* Advanced templating and chaining variable manipulation\*\*. I would like to add here comment to the following  
"# Notice that start\_hour was just defined and can be used! The order that they’re declared does matter. "  
start\_hour variable can be defined anywhere - ansible lazily evaluates variables - it can be defined in vars section in playbooks, defaults/main.yml file in the role, in the inventory. Literally in any place where you can define the variable for this code to work. Only caveat is that you need variable to be available in the scope you will be using them and take into account variable precedence ([Using variables — Ansible Community Documentation](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#understanding-variable-precedence)).

Example \*\* Using `failed_when` without a register\*\* I like, and not for the reason it is less to type, but because it allows not to pollute “global” variables scope with registers if they are only needed for failed\_when, changed\_when and similar single task attributes.

There is an interesting suggestion from @felixfontein in regards to ansible variables scope. [More ways to set variables for a scope](https://forum.ansible.com/t/more-ways-to-set-variables-for-a-scope/44433)  
Maybe some day there will be option to register and load variables only to the certain scope (host, host\_group, block, task, role, etc) not to pollute “global” scope.

---

<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:** [December 6, 2025, 9:38pm UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965/3 "2025-12-06T21:38:27Z")

</div>

I really like the new features. As a plugin developer I was interested a lot in being able to set variables from plugins, and as a user I really like to avoid unnecessary temporary variables. I don’t think anyone will argue that having a public API (that’s also used by ansible-core plugins) for plugins to set variables is a bad idea, so I’ll concentrate on the register projections for the remainder of this post.

It simplifies a common pattern, and avoids polluting the global namespace with temporary variables. While I would prefer to combine this with scopes (like being able to say that the result of one projection is only valid for the remainder of the task file / block / role / …, instead of being globally available), this is already a lot better than status quo. I’ve seen a lot of places in roles (both collection integration tests and actual production roles and playbooks) that can be simplified with this, and I’m looking forward to be able to use this!

---

<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:** [December 6, 2025, 9:49pm UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965/4 "2025-12-06T21:49:55Z")

</div>

> [@felixfontein](#):
>
> As a plugin developer I was interested a lot in being able to set variables from plugins

isn’t is possible already with action plugins?

---

<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:** [December 6, 2025, 10:07pm UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965/5 "2025-12-06T22:07:08Z")

</div>

No, you can only set facts (though with data tagging you can now set facts whose value is lazily evaluated), but not variables. (And facts are affected by the `INJECT_FACTS_AS_VARS`’s default value deprecation, see [https://forum.ansible.com/t/44886.](https://forum.ansible.com/t/44886.))

---

<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:** [December 7, 2025, 10:56am UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965/6 "2025-12-07T10:56:22Z")

</div>

> [@felixfontein](#):
>
> No, you can only set facts

> [@felixfontein](#):
>
> but not variables

I was always certain that facts are variables with different precedence. Isn’t facts and registered variables are essentially one thing? They are mentioned in one line in variable precedence docs, so you can overwrite one with another.  
“19. Registered vars and set\_facts” ( [Using variables — Ansible Community Documentation](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id44) )

What is the subtle difference between facts and registered variables I do not see?

This is not what I was exactly referring to in my initial comment, I was referring to extended `register` logic for tasks not new public API inside AnsibleBase to set variables. If I understand correctly you are referring to following

```python
class VariableLayer(enum.Enum):
    CACHEABLE_FACT = enum.auto()
    EPHEMERAL_FACT = enum.auto()
    INCLUDE_VARS = enum.auto()
    REGISTER_VARS = enum.auto()

# ...

    def register_host_variables(self, variables: dict[str, object], layer: VariableLayer = VariableLayer.REGISTER_VARS) -> None:
        """
        Register the given variables for the current host at the specified variable precedence layer.
        Calling this method more than once for the same layer will override any previous value for the currently executing action.
        """

```

As I understand this will allow module developers not only return module result (for it to be registered with old or new register functionality) and create another side effect for the controller - set variables.  
And in my mind this is equivalent to following python code

```python
b = f(a)
print(c)

```

a - module parameters  
f - module  
b - module registered variable  
c - variable that is set explicitly by module (function f)  
And from logical and language perspective it does not make any sense to me. I do not know any language where it is encouraged to set “global” state (variables) from within function (module).

How to avoid race condition where `include_vars` are set from one module running on several hosts and all the values that are set will be different - meaning value of variable set this way will depend on the order in which modules are executed on the hosts. `include_vars` are scoped globally - they are available even on another host.  
How to handle loop situation where same (presumably) variable will be set by module several times for each loop iteration (so it will be overwritten).

Also 3 out of 4 variable precedence layers can be used explicitly already today: CACHEABLE\_FACT, EPHEMERAL\_FACT with set\_facts, REGISTER\_VARS with `register` keyword for tasks. I bet we already can even leverage `include_vars` right after the module call and include variable explicitly and get similar (if not the same result). For instance

```ansible
- shell: echo hello
  register: echo_result
- include_vars:
  file: echo_result.yml

# cat vars/echo_result.yml
echo_result_include_var: "{{ echo_result }}" # using echo_result for variable here is not very useful, because include_vars has lower precedence than registered variables

```

And this is done explicitly (zen of python says that explicit is better than implicit, although it was not included into zen of Ansible).

I agree that is more verbose than in the PR, but on the other hand it is more explicit (better from my point of view). Also it becomes evident that include\_vars might benefit from parameter to set variables directly (not read from the file, similar to set\_facts). And set\_facts might benefit from `file` parameter to make them uniform.

---

<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:** [December 7, 2025, 11:01am UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965/7 "2025-12-07T11:01:46Z")

</div>

> [@nitzmahone](#):
>
> It adds a new implicit variable (`_task`) for the `register` keyword and task conditionals (e.g., `failed_when`, `changed_when`, `break_when`, `until`)

I was almost certain that I saw similar thought experiment somewhere. Initially I thought it was zen of Ansible, but it is different presentation [https://youtu.be/JlrkizEBjXk?si=F\_cKtRyGpBJoWPUN&t=1169](https://youtu.be/JlrkizEBjXk?si=F_cKtRyGpBJoWPUN&t=1169).

There are some compelling arguments to not having `_task` (they call it `res` in presentation)

---

<div class="post-metadata">

**Author:** ![flowerysong](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/flowerysong/32/2305_2.png) [@flowerysong](https://forum.ansible.com/u/flowerysong)\
**Post date:** [December 7, 2025, 5:58pm UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965/8 "2025-12-07T17:58:14Z")

</div>

> [@kks](#):
>
> I was always certain that facts are variables with different precedence. Isn’t facts and registered variables are essentially one thing? They are mentioned in one line in variable precedence docs, so you can overwrite one with another.  
> “19. Registered vars and set\_facts” ( [Using variables — Ansible Community Documentation](https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html#id44) )
> 
> What is the subtle difference between facts and registered variables I do not see?

The difference is that they are completely different things. We were talking about what plugins can do, which currently is only to create facts (11 on the list.) `set_facts` is a built-in action that does _not_ create facts, it creates variables (except when `cacheable` is true and it creates both a fact and a variable.) As stated in the PR description, this ability to create variables relies on special handling by the engine that other action plugins do not receive.

Note that injecting facts (untrusted data returned from remote module execution) into the top-level variable namespace is a misfeature that has hung around for backwards compatibility reasons. There’s been a [switch that turns it off](https://docs.ansible.com/projects/ansible/latest/reference_appendices/config.html#inject-facts-as-vars) for ages, and starting in [2.20](https://docs.ansible.com/projects/ansible-core/devel/porting_guides/porting_guide_core_2.20.html#inject-facts-as-vars) people are warned if they access injected facts without explicitly requesting that the injection be done.

There are many cases (like [community.sops.load\_vars](https://docs.ansible.com/projects/ansible/latest/collections/community/sops/load_vars_module.html)) where it’s useful for controller-side plugins (local execution of trusted code) to create variables, and in the old injected-facts world they were usually able to get close enough to the desired functionality by creating facts. In order to feasibly get rid of fact injection entirely, an alternative needs to be provided.

---

<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:** [December 7, 2025, 6:14pm UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965/9 "2025-12-07T18:14:27Z")

</div>

When I started answersing some of the questions I didn’t yet see @flowerysong’s answer which provides most of these answers already, so I’ve removed them from this post. I’ll just keep the extra things 🙂

Some background on why the action is called ansible.builtin.set\_fact and not ansible.builtin.set\_var etc. can be found here: [INJECT\_FACTS\_AS\_VARS is defaulting to False in 2.24 - #5 by bcoca](https://forum.ansible.com/t/inject-facts-as-vars-is-defaulting-to-false-in-2-24/44886/5)

> [@kks](#):
>
> This is not what I was exactly referring to in my initial comment, I was referring to extended `register` logic for tasks not new public API inside AnsibleBase to set variables. If I understand correctly you are referring to following

In my first post I was not replying to your post, but to this thread, which is about both new features.

> [@kks](#):
>
> As I understand this will allow module developers not only return module result (for it to be registered with old or new register functionality) and create another side effect for the controller - set variables.

Just to make this clear: modules **cannot** use this API. The only plugins which can use this API are action plugins (that always run on the controller). These can run modules and upgrade their return values to variables, if someone desires so and thinks it is a good idea - which usually it is not, for security reasons.

> [@flowerysong](#):
>
> As stated in the PR description, this ability to create variables relies on special handling by the engine that other action plugins do not receive.

If anyone is interested in how/where that happens: [ansible/lib/ansible/plugins/strategy/\_\_init\_\_.py at e25cc345feda25dc8a05e25b06c09c1c426d011b · ansible/ansible · GitHub](https://github.com/ansible/ansible/blob/e25cc345feda25dc8a05e25b06c09c1c426d011b/lib/ansible/plugins/strategy/ __init__.py#L692-L720)

---

<div class="post-metadata">

**Author:** ![apollo13](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/apollo13/32/7847_2.png) [@apollo13](https://forum.ansible.com/u/apollo13)\
**Post date:** [December 9, 2025, 8:19am UTC](https://forum.ansible.com/t/fallible-2025-12-5-released-with-register-projections-and-new-action-api-previews/44965/10 "2025-12-09T08:19:44Z")

</div>

I don’t have much to add, but I really like the register projections – thank you for the work there!
