The Terraform try() function evaluates a sequence of expressions in order and returns the result of the first one that does not produce an error. It’s most commonly used to safely access optional or nested attributes that may not exist at runtime, falling back to a default value instead of failing the plan.
Introduced in Terraform 0.12.20 (February 2020), try() works alongside the related can() function, which evaluates an expression and returns a boolean indicating whether it succeeded.
We will explain what they are, and what they are used for, as well as run through some examples to show their usage. We will then describe how they compare to the lookup function.
What does try function do in Terraform?
Terraform try evaluates all of its argument expressions in turn and returns the result of the first one that does not produce any errors. The try function can only catch and handle dynamic errors resulting from access to data that isn’t known until runtime. The try function is intended only for concise testing of the presence of and types of object attributes. It can technically accept any sort of expression.
How to use Terraform try?
The syntax is as follows:
try(expression, expression, ...)It evaluates each expression in order and returns the result of the first one that doesn’t produce an error. If all of them error, try itself errors.
For example:
locals {
jedi_map = {
darth = "sith"
}
try1 = try(local.jedi_map.boba_fett, "jedi")
}
output "try1" {
value = local.try1
}The output of this code will be "jedi", because local.jedi_map.boba_fett does not exist. try() catches the runtime error and returns the fallback value.
If the value of the try function used to evaluate the condition does not exist, then it will error. In the below example, local.nothing.here is not declared in my configuration, and so the condition produces an error. It will not catch errors relating to expressions that can be proven to be invalid for any input, such as a malformed resource reference.
Two behaviors of try() that often trip people up:
- null is not an error.
try(null, "fallback")returnsnull, not"fallback", because a successful evaluation that produces null still counts as success. If you also want to treatnullas a missing value, wrap the call withcoalesce():coalesce(try(local.value, null), "fallback"). - If every expression fails,
try()returns an error listing all the failures, not just the last one. The message is formatted as"no expression succeeded:"followed by each failed expression’s error, which is useful for debugging when you can’t tell which fallback path you expected to work.
Terraform try limitations and best practices
Let’s now take a look at some limitations and best practices for the Terraform try function:
tryshould be used when you don’t know if the data will exist or how it is going to be formed.- Hashicorp recommends to use the
tryfunction only in special local values whose expressions perform normalization. To make sure that the error handling is confined to a single location in the module and the rest of the module can just use straightforward references to the normalized structure and thus be more readable for future maintainers. - Although
trycan technically accept any sort of expression it is recommended to use it only with simple attribute references and type conversion functions. - Always try to keep your code simple to promote readability. Overuse of
tryto suppress errors will lead to a configuration that is hard to understand and maintain. - Consider
optional()first for object-typed variables. Since Terraform 1.3, you can declare optional attributes with defaults directly in a variable’s type constraint — for example,tags = optional(map(string), {}). This is often cleaner than wrapping every access intry()and is HashiCorp’s recommended pattern for optional inputs.
Check out other Terraform best practices to improve your Terraform workflow.
Using Terraform try — examples
In this example, try() is used to fall back to a default set of Kubernetes namespaces when the variable doesn’t define one.
Note the use of toset(). for_each requires a set or map, not a list.
variable "config" {
type = object({
namespaces = optional(set(string))
})
default = {}
}
resource "kubernetes_namespace" "ns" {
for_each = try(
toset(var.config.namespaces),
toset(["app-frontend", "app-backend"])
)
metadata {
name = each.value
}
}In the next example, try is used to deal with situations where a value might be provided in two different forms, allowing us to normalize to the most general form.
The variable accepts any type and the try function then uses the tostring and tolist expressions to try a conversion to a string or a list.
variable "jedi_list" {
type = any
}
locals {
example = try(
[tostring(var.jedi_list)],
tolist(var.jedi_list),
)
}In the last example, we show how multiple values can be tested using the try function. A locals map is specified with a key value pair of luke = "jedi" — we then test for two unspecified values, before the third one will be used as it exists. The code below outputs jedi .
locals {
jedi = {
luke = "jedi"
}
}
output "try" {
value = try(local.jedi.bar, local.jedi.baz, local.jedi.luke, "not_a_jedi")
}Normalizing external data with try()
The most common production use case for try() is normalizing data loaded from external files, JSON, YAML, or API responses, where some fields may be missing. Setting fallbacks in a single locals block keeps error handling in one place and lets the rest of your module treat the data as if it were always present.
locals {
raw_config = yamldecode(file("${path.module}/config.yaml"))
config = {
name = tostring(try(local.raw_config.name, "default-app"))
environment = tostring(try(local.raw_config.environment, "development"))
tags = try(local.raw_config.tags, {})
replicas = try(local.raw_config.replicas, 1)
}
}Anywhere else in the module, you can now reference local.config.tags or local.config.replicas without re-checking whether they exist.
Terraform try vs lookup
Terraform try is a more general form of the lookup function in Terraform. The lookup function is used to retrieve the value of a map element or list element.
Usage:
lookup(map, key, [default])map: The map from which to look up the value.key: The key to look up in the map.default(optional): The value to return if the key is not found in the map. If not specified and the key is not found,nullis returned.
Terraform lookup example:
variable "jedi_map" {
type = map(string)
default = {
luke = "jedi",
darth = "sith",
}
}
output "result" {
value = lookup(var.jedi_map, "luke", "sith")
}In this example, lookup is used to retrieve the value associated with the key “luke” from the map var.jedi_map. If the key is not found, it returns the default value “sith”. In this case, it will be found and return “jedi”.
See also Terraform try vs coalesce function.
What is Terraform can function?
Terraform’s can() function evaluates a given expression and returns a boolean, true if it succeeds and false if it produces errors. It’s primarily designed for variable validation rules, where you need a simple pass/fail check rather than a fallback value.
How to use Terraform can?
Let’s look at an example:
> local.jedi
{
"yoda" = "jedi"
}
> can(local.jedi.yoda)
true
> can(local.jedi.darth)
falseIn this example, a local map is declared with the key of “yoda” and the value of “jedi”. The can function tests whether the value exists and returns true if it does, and false if it does not.
Similar to the try function, if the value is not declared, then an error message will be produced:
Using Terraform can — example
The example below shows how to use the can function in variable validation to ensure a timestamp is valid. The condition will fail if the second argument is not a valid timestamp, using the formatdate function.
variable "timestamp" {
type = string
validation {
condition = can(formatdate("", var.timestamp))
error_message = "The timestamp is invalid."
}
}Key points
In summary Terraform try evaluates all of its argument expressions in turn and returns the result of the first one that does not produce any errors and can evaluates the given expression and returns a boolean value indicating whether the expression produced a result without any errors.
Terraform is really powerful, but managing imports, state, and configuration at scale requires more than the CLI alone. Spacelift takes Terraform management to the next level by giving you access to a powerful CI/CD workflow and features such as:
- Policy as code (based on Open Policy Agent) to enforce guardrails on every plan and apply
- Drift detection to catch resources that have changed outside of Terraform
- Multi-IaC workflows across Terraform, OpenTofu, CloudFormation, Pulumi, and Kubernetes
- Self-service infrastructure through Blueprints and Templates
- Full audit trails so you always know what changed, when, and who approved it
If you want to learn more about Spacelift, create a free account today or book a demo with one of our engineers.
Note: New versions of Terraform are placed under the BUSL license, but everything created before version 1.5.x stays open-source. OpenTofu is an open-source version of Terraform that expands on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6.
Manage Terraform Better with Spacelift
If you are struggling with Terraform automation and management, check out Spacelift. It helps you manage Terraform state, build more complex workflows, and adds several must-have capabilities for end-to-end infrastructure management.
Frequently asked questions
What version of Terraform supports try() and can()?
Both functions were introduced in Terraform 0.12.20 (January 2020) and are available in every release since, including Terraform 1.x.
Does Terraform try() catch every kind of error?
No. It only catches dynamic errors that surface at runtime, such as missing attributes or type mismatches. Static problems like malformed references or undeclared resources still fail outright.
What's the difference between Terraform try() and can()?
try() evaluates a sequence of expressions and returns the first one that succeeds, making it useful for fallback values. can() evaluates a single expression and returns a boolean indicating success, mainly used inside variable validation rules.
What's the difference between Terraform try() and lookup()?
lookup() retrieves a value from a map by key and returns a default if the key is absent, so it works on maps only. try() handles any dynamic evaluation error, including missing object attributes, type conversion failures, or null traversals.
Does Terraform try() treat null as an error?
No. null is a valid value, not an error, so try() returns it as the successful result rather than moving to the next expression. To fall back when a value is null, combine try() with coalesce() or an explicit check.
HashiCorp Developer | Terraform Docs. try – Functions – Configuration Language. Accessed: 14 May 2026
HashiCorp Developer | Terraform Docs. can – Functions – Configuration Language. Accessed: 14 May 2026
HashiCorp Developer | Terraform Docs. lookup – Functions – Configuration Language. Accessed: 14 May 2026
