--- /dev/null
+= The finally section
+
+.Syntax
+[source,unlang]
+----
+finally [<request-type>] {
+ [ statements ]
+}
+----
+
+.Description
+The intent of the `finally { ... }` section is to allow any logging or cleanup
+to be peformed before the request exits the virtual server. For example,
+if you wish to log all requests and responses that enter a virtual server,
+you could place a single call to `linelog` in the finally section.
+
+The `finally { ... }` section is executed last before a request exits a
+virtual server. It is executed regardless of whether the request was
+accepted, rejected or timed out.
+
+The `<request-type>` qualifier can be any valid request type for the protocol the
+virtual server implments. If a no request-type-specific finally section
+is provided, the unqualified `finally { ... }` section is executed.
+
+.Example of using unqalified finally
+[source,unlang]
+----
+# Executes first
+recv Access-Request {
+ ...
+}
+
+# Executes second
+send Access-Accept {
+ ...
+}
+
+# Executes third
+finally {
+ linelog.log("Sending %{reply.Packet-Type} to %{NAS-IP-Address}")
+}
+----
+
+.Example of using qualified finally
+[source,unlang]
+----
+# Executes first
+recv Access-Request {
+ ...
+}
+
+# Executes second
+send Access-Accept {
+ ...
+}
+
+# Executes third
+finally Access-Request {
+ linelog.log("Sending %{reply.Packet-Type} in response to Access-Request to %{NAS-IP-Address}")
+}
+
+# Ignored for Access-Requests, would be called for Accounting-Requests
+finally {
+ ...
+}
+----
+
+== Rcodes
+=== Available to finally
+The rcode returned by the section executed immediately prior to finally is
+preserved, and can be checked within the finally section using
+xref:unlang/condition/return_codes.adoc[return code expressions].
+This allows different behaviour based on the result of the protocol state
+machine.
+
+.Example of calling different modules based on the previous rcode
+[source,unlang]
+----
+# Executes first
+recv Access-Request {
+ ...
+}
+
+send Access-Accept {
+ ok
+}
+# returns `ok`
+
+finally {
+ if (ok || updated) {
+ linelog.log("Request succeeded")
+ } else {
+ linelog.log("Request failed")
+ }
+}
+----
+
+=== Returned by finally
+The return code of `finally { ... }` sections will not modify the response packet
+type from the virtual server. i.e. a `reject` rcode will not result an
+`Access-Reject` response being sent for RADIUS. This is because `finally` sections
+run _after_ the protocol state machine as completed.
+
+If, however, a virtual server containing a `finally { ... }` section is called,
+the rcode returned by finally will be the rcode returned by the virtual server
+and the `call` keyword.
+
+== Timeouts
+
+The `finally` section is executed even if the request times out. Requests
+terminated by a timeout will have the `timeout` rcode set. This condition
+can be checked with `if (timeout)` at the start of the section.
+
+[NOTE]
+====
+Request passing through a `finally { ... }` section will artificially extend
+the lifetime of the request until the section completes. To ensure
+the server remains responsive, any potentially long running module calls or
+xlat expansions should be wrapped in a xref:unlang/timeout.adoc[timeout] block.
+====
+
+== Subrequests
+
+Where xref:unlang/subrequest.adoc[subrequest] calls are used, the
+`finally { ... }` section in the parent will not be executed until the subrequest
+has completed. Timeouts in `finally { ... }` sections in subrequests should be set
+appropriately short to ensure that the parent request isn't cancalled due to an
+excessively long running subrequest.
+
+// Copyright (C) 2025 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// This documentation was developed by Network RADIUS SAS.