Repository: askama-rs/askama (Rust)

Issue:
Nested includes are not handled properly
**Describe the bug**

When an included template includes another, various bugs can occur depending on the actual directory structure:

* file not found in some cases;
* recursive loop in other cases.


**To Reproduce**

Rust code:

```rust
use askama::Template;

#[derive(Template)]
#[template(path = "include-nested.html")]
struct IncludeTemplate<'a> { strs: &'a [&'a str] }

fn main() {
    let strs = vec!["foo", "bar"];
    let s = IncludeTemplate { strs: &strs };
    assert_eq!(s.render().unwrap(), "\n  NESTED: INCLUDED: foo\n  NESTED: INCLUDED: bar");
}
```

With the following template structure:

```
templates/include-nested.html
templates/a/included.html
templates/a/b/included.html
```

With `include-nested.html` being:

```
{% for s in strs %}
  {% include "a/included.html" %}
{%- endfor %}
```

With `a/included.html` being:

```
NESTED: {% include "b/included.html" %}
```

And `a/b/include.html` being:

```
INCLUDED: {{ s }}
```

Compilation of the template gives this error: 

```
template "b/included.html" not found in directories ["[...]/templates"]
```


**To Reproduce (another case)**

When compiling the same example with the following directory structure (note the nested `a` directories):

```
templates/include-nested.html
templates/a/included.html
templates/a/a/included.html
```

It enters a recursive loop:

```
0   libaskama_derive-770e8a134d7b4d73.d 0x0000000110c87ea7 _ZN13askama_derive9generator4node53_$LT$impl$u20$askama_derive..generator..Generator$GT$6handle17hd5192a26f815ca7dE + 1463
1   libaskama_derive-770e8a134d7b4d73.d 0x0000000110cf7772 _ZN13askama_derive9generator4node53_$LT$impl$u20$askama_derive..generator..Generator$GT$14handle_include28_$u7b$$u7b$closure$u7d$$u7d$17hc539529d0506ad63E + 98
2   libaskama_derive-770e8a134d7b4d73.d 0x0000000110c870b1 _ZN13askama_derive9generator4node53_$LT$impl$u20$askama_derive..generator..Generator$GT$10with_child17hc82848a2f39773e8E + 561
3   libaskama_derive-770e8a134d7b4d73.d 0x0000000110c8cf80 _ZN13askama_derive9generator4node53_$LT$impl$u20$askama_derive..generator..Generator$GT$14handle_include17ha6c6107538ce549dE + 1424
etc...
```

**Askama version**

`0.14`

**Rust version**

`1.90.0 (1159e78c4 2025-09-14)`



Discussion:
Will send a PR right away



Patch:
diff --git a/askama_derive/src/generator/node.rs b/askama_derive/src/generator/node.rs
index 7462c8637..233c247ed 100644
--- a/askama_derive/src/generator/node.rs
+++ b/askama_derive/src/generator/node.rs
@@ -743,7 +743,7 @@ impl<'a> Generator<'a, '_> {
             .map(|path| FileInfo::of(i.span(), path, ctx.parsed));
         let path = self.input.config.find_template(
             i.path,
-            Some(&self.input.path),
+            Some(ctx.path.unwrap_or(self.input.path.as_ref())),
             file_info,
             Some(ctx.span_for_node(i.span())),
         )?;


Test patch:
diff --git a/testing/templates/include-nested.html b/testing/templates/include-nested.html
new file mode 100644
index 000000000..ff4d0b27f
--- /dev/null
+++ b/testing/templates/include-nested.html
@@ -0,0 +1,3 @@
+{% for s in strs %}
+  {% include "nested/included.html" %}
+{%- endfor %}
diff --git a/testing/templates/nested/included.html b/testing/templates/nested/included.html
new file mode 100644
index 000000000..baf2abbd4
--- /dev/null
+++ b/testing/templates/nested/included.html
@@ -0,0 +1,1 @@
+NESTED: {% include "sub-nested/included.html" %}
diff --git a/testing/templates/nested/sub-nested/included.html b/testing/templates/nested/sub-nested/included.html
new file mode 100644
index 000000000..ff10488ff
--- /dev/null
+++ b/testing/templates/nested/sub-nested/included.html
@@ -0,0 +1,1 @@
+INCLUDED: {{ s }}
diff --git a/testing/tests/include.rs b/testing/tests/include.rs
index 9d8c9558b..d591df712 100644
--- a/testing/tests/include.rs
+++ b/testing/tests/include.rs
@@ -102,3 +102,19 @@ fn block_in_include() {
         "block_in_partial: from partial!\n"
     );
 }
+
+#[test]
+fn test_nested_includes() {
+    #[derive(Template)]
+    #[template(path = "include-nested.html")]
+    struct IncludeTemplate<'a> {
+        strs: &'a [&'a str],
+    }
+
+    let strs = vec!["foo", "bar"];
+    let s = IncludeTemplate { strs: &strs };
+    assert_eq!(
+        s.render().unwrap(),
+        "\n  NESTED: INCLUDED: foo\n  NESTED: INCLUDED: bar"
+    );
+}