| Tested on | Ubuntu 26.04 LTS (Resolute Raccoon) |
|---|---|
| Package | terraform 1.15.8-1 |
| Applies to | Any host with Terraform installed |
| Lab environment | Single Ubuntu VM with Terraform — Terraform lab environment on Ubuntu |
| Privilege | Normal user |
| Scope | Exact-error fix for Functions may not be called here — variable default restrictions, timestamp example, locals workaround, direct function expressions, dynamic null-default fallback, variable versus local distinction, verified backend restriction, timestamp plan drift, and common mistakes. Does not cover the full Terraform function catalog, try and can tutorials, or general HCL syntax. |
| Related guides | Terraform locals Terraform variables Terraform functions Terraform expressions known after apply |
Terraform prints Functions may not be called here when you place a function call in a configuration argument that must stay static during early processing. The most common trigger is timestamp() inside a variable default.
This article reproduces that error on Terraform 1.15.8, shows where the expression belongs instead, and documents two other verified restrictions. It is not a general Terraform functions catalog.
~/terraform-labs/terraform-functions-may-not-be-called-here/. Failures are in errors/; working fixes are in fixes/ and demos/. Examples use the built-in terraform_data resource — no cloud provider required.
Reproduce "Functions May Not Be Called Here"
The failing configuration puts timestamp() in a variable default:
mkdir -p ~/terraform-labs/terraform-functions-may-not-be-called-here/errors/variable-default-timestamp
cd ~/terraform-labs/terraform-functions-may-not-be-called-here/errors/variable-default-timestampcat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
variable "timestamp" {
type = string
default = timestamp()
}
resource "terraform_data" "example" {
input = var.timestamp
}
EOFOn Terraform 1.15.8 the configuration must be valid before initialization completes, so the error appears during terraform init rather than a separate validate step:
terraform init -input=falseError: Function calls not allowed
on main.tf line 7, in variable "timestamp":
7: default = timestamp()
Functions may not be called here.The default argument requires a literal value and cannot reference other configuration objects. Function calls therefore belong in expression-capable contexts such as locals or resource arguments rather than in the variable default.
Move the calculated value to locals
The usual fix is to calculate the value in a locals block and reference that local from resources.
mkdir -p ~/terraform-labs/terraform-functions-may-not-be-called-here/fixes/locals-timestamp
cd ~/terraform-labs/terraform-functions-may-not-be-called-here/fixes/locals-timestampcat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
locals {
timestamp = timestamp()
}
resource "terraform_data" "example" {
input = local.timestamp
}
EOFInitialize the fixed configuration:
terraform init -input=falseTerraform has been successfully initialized!Validate confirms the function call is now in an allowed context:
terraform validate -no-colorSuccess! The configuration is valid.Plan and apply to store the computed timestamp:
terraform apply -auto-approve -input=false -no-colorApply complete! Resources: 1 added, 0 changed, 0 destroyed.Inspect what landed in state:
terraform state show terraform_data.exampleinput = "2026-08-13T03:57:23Z"
output = "2026-08-13T03:57:23Z"The distinction in plain terms:
variable default → static fallback input the caller can override
local → calculated expression inside the moduleDestroy when you finish so the lab directory stays disposable:
terraform destroy -auto-approve -input=false -no-colorUse functions directly where expressions are allowed
You do not always need a locals block for a one-off calculation. Resource arguments accept function calls when Terraform evaluates them during the plan/apply graph.
mkdir -p ~/terraform-labs/terraform-functions-may-not-be-called-here/fixes/direct-expression
cd ~/terraform-labs/terraform-functions-may-not-be-called-here/fixes/direct-expressioncat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
resource "terraform_data" "example" {
input = formatdate("YYYY-MM-DD", timestamp())
}
EOFInitialize and plan before the first apply:
terraform init -input=falseterraform plan -no-color -input=false# terraform_data.example will be created
+ resource "terraform_data" "example" {
+ input = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.formatdate() and timestamp() are allowed here because terraform_data.input accepts normal expressions. However, timestamp() has special evaluation behavior: Terraform treats its result as unknown during planning and resolves the actual timestamp during apply. Therefore an expression derived from timestamp(), such as formatdate(..., timestamp()), can also remain (known after apply) in the plan. See known after apply for the broader model.
Apply and read the stored date string:
terraform apply -auto-approve -input=false -no-colorterraform state show terraform_data.exampleinput = "2026-08-13"That does not mean every block allows arbitrary expressions — backend settings and variable defaults are stricter, as shown later.
Dynamic defaults with a null variable and a local
When you want caller override with a calculated fallback, leave the variable default static and branch in locals:
mkdir -p ~/terraform-labs/terraform-functions-may-not-be-called-here/fixes/dynamic-fallback
cd ~/terraform-labs/terraform-functions-may-not-be-called-here/fixes/dynamic-fallbackcat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
variable "created_at" {
type = string
default = null
}
locals {
created_at = var.created_at != null ? var.created_at : timestamp()
}
resource "terraform_data" "example" {
input = local.created_at
}
EOFPlan without setting the variable — Terraform selects the calculated fallback expression, but the value returned by timestamp() remains unknown until apply:
terraform init -input=falseterraform plan -no-color -input=false# terraform_data.example will be created
+ input = (known after apply)
Plan: 1 to add, 0 to change, 0 to destroy.Pass an explicit value to skip the calculated fallback:
terraform plan -no-color -input=false -var created_at=fixed-value+ input = "fixed-value"
Plan: 1 to add, 0 to change, 0 to destroy.The null default is literal and valid. The function call lives in locals, where expressions are allowed.
timestamp() returns a new value on every evaluation. When that value is written into state through a resource argument, later plans can show perpetual drift. Use a fixed string for identity fields; reserve timestamp() for one-time labels or debugging, not stable resource keys.
Repeated plans when timestamp() feeds a resource
After apply, a locals { timestamp = timestamp() } value stored in terraform_data.input makes every later plan propose an update.
mkdir -p ~/terraform-labs/terraform-functions-may-not-be-called-here/demos/repeated-plan
cd ~/terraform-labs/terraform-functions-may-not-be-called-here/demos/repeated-plancat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
locals {
timestamp = timestamp()
}
resource "terraform_data" "example" {
input = local.timestamp
}
EOFterraform init -input=falseterraform apply -auto-approve -input=false -no-colorWith the stack applied, run the first plan pass:
terraform plan -no-color -input=false# terraform_data.example will be updated in-place
~ resource "terraform_data" "example" {
~ input = "2026-08-13T03:57:28Z" -> (known after apply)
~ output = "2026-08-13T03:57:28Z" -> (known after apply)
}
Plan: 0 to add, 1 to change, 0 to destroy.A second immediate plan shows the same one-change result, and waiting one second does not clear it. The local is valid HCL, but storing a changing expression in terraform_data.input makes every plan want an update. That is expected behavior, not a bug in locals.
Other contexts that restrict function calls
Only include restrictions verified on Terraform 1.15.8 in this lab. Two contexts failed with the same Functions may not be called here message; one related mistake produced different wording.
Variable default (primary case)
Already covered above. Any function call in default fails — including try() with literal arguments:
mkdir -p ~/terraform-labs/terraform-functions-may-not-be-called-here/errors/try-in-variable-default
cd ~/terraform-labs/terraform-functions-may-not-be-called-here/errors/try-in-variable-defaultcat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
variable "port" {
type = number
default = try(tonumber("8080"), 80)
}
EOFterraform init -input=falseError: Function calls not allowed
on main.tf line 7, in variable "port":
7: default = try(tonumber("8080"), 80)
Functions may not be called here.Wrapping the call in try() does not change the restriction. Move the logic to locals or a resource argument.
Referencing another variable in a default
A different error — variables are also banned from defaults:
mkdir -p ~/terraform-labs/terraform-functions-may-not-be-called-here/errors/variable-default-var-ref
cd ~/terraform-labs/terraform-functions-may-not-be-called-here/errors/variable-default-var-refcat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
variable "base" {
type = string
}
variable "name" {
type = string
default = "${var.base}-suffix"
}
EOFterraform init -input=falseError: Variables not allowed
on main.tf line 11, in variable "name":
11: default = "${var.base}-suffix"
Variables may not be used here.Use a locals block to combine values instead of chaining variables through defaults.
Backend block path
Backend settings are loaded before the rest of the configuration, so arguments must be static:
mkdir -p ~/terraform-labs/terraform-functions-may-not-be-called-here/additional-tests/backend-join
cd ~/terraform-labs/terraform-functions-may-not-be-called-here/additional-tests/backend-joincat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
backend "local" {
path = join("/", ["terraform.tfstate"])
}
}
EOFterraform init -input=falseError: Function calls not allowed
on main.tf line 5, in terraform:
5: path = join("/", ["terraform.tfstate"])
Functions may not be called here.Fix: use a literal path such as path = "terraform.tfstate" or configure the backend through a partial config file with -backend-config.
Where functions are allowed (contrast)
Not every block restricts functions. An output value may call timestamp():
mkdir -p ~/terraform-labs/terraform-functions-may-not-be-called-here/additional-tests/output-with-function
cd ~/terraform-labs/terraform-functions-may-not-be-called-here/additional-tests/output-with-functioncat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
output "ts" {
value = timestamp()
}
EOFterraform init -input=falseterraform plan -no-color -input=falseChanges to Outputs:
+ ts = (known after apply)Resource and most locals expressions are the normal homes for function calls. See Terraform expressions for evaluation order; this article stays focused on the error case.
Common mistakes
These patterns repeatedly produce the error or a sibling restriction:
- Calling a function inside a variable
default— use a literal default ornull, then compute inlocals - Referencing another variable from a
default— uselocalsto derive combined values - Treating a variable
defaultlike runtime program logic — defaults are static module inputs, not a place for calculations - Using
timestamp()for fields that should stay stable in state — expect perpetual plan changes - Wrapping the call in
try()to dodge the error —tryis still a function call in a forbidden context - Moving logic to
localsbut still wondering why plan always changes — the local is valid; the changing value is the problem
Decision guide:
Static caller-facing default → variable default (literal only)
Calculated or reusable value → locals
One-off in a resource argument → function call directly in that argumentTroubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Functions may not be called here on variable default |
Function call in variable default | Move expression to locals or a resource argument |
Variables may not be used here on variable default |
Default references another variable | Combine values in locals instead |
try(...) in default still fails |
try is a function |
Same fix as any other function in defaults |
Error during terraform init on backend |
Function in backend block argument |
Use a literal path or -backend-config |
Plan always wants to update terraform_data after using timestamp() |
Value changes every evaluation | Use a stable value, or accept perpetual plan changes |
Function works in output but not in variable |
Different expression contexts | Not every block has the same rules — match the pattern to the block type |
References
- Input variables — literal
defaultrequirement - Local values — where to place calculated expressions
- Functions — function reference (not repeated here)
- Backend configuration — static backend arguments
Summary
Functions may not be called here means Terraform found a function in an argument that must be known before dynamic evaluation — most often timestamp() or try() inside a variable default. Variable defaults accept literals only; move calculations to locals or call functions directly in resource arguments where expressions are allowed.
timestamp() is allowed in those expression contexts, but its result is unknown during planning and resolved during apply — expressions built from it can show (known after apply) and cause perpetual drift when stored in state. For caller override with a dynamic fallback, default the variable to null and branch in locals. Backend path arguments follow the same static rule.
If you need broader function coverage, read Terraform functions and Terraform locals. This page stays scoped to the exact error and its verified fixes.

