Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Terraform By James Joyner IV · · 9 min read

Terraform Error Guide: 'Invalid function argument' (bad type or value passed to a function)

Fix Terraform's 'Invalid function argument' error: convert types with tostring/tonumber/tolist, fix cidrsubnet ranges, guard inputs with try()/can(), test in console.

  • #terraform
  • #troubleshooting
  • #errors
  • #functions

Exact Error Message

Invalid function argument fires when a built-in function receives a value it cannot use — the wrong type, an out-of-range number, a missing file path, or malformed JSON/YAML. Terraform names the function and the parameter that rejected the value.

Error: Invalid function argument

  on network.tf line 8, in locals:
   8:   subnet = cidrsubnet(var.vpc_cidr, 12, 4096)

Invalid value for "netnum" parameter: prefix extension of 12 does not
accommodate a subnet numbered 4096.
Error: Invalid function argument

  on data.tf line 3, in locals:
   3:   cfg = jsondecode(file("${path.module}/config.json"))

Invalid value for "str" parameter: contains an invalid JSON encoding:
invalid character '}' looking for beginning of object key string.

What the Error Means

Terraform’s functions are strictly typed. When you call cidrsubnet, file, jsondecode, element, templatefile, or regex, the runtime validates each argument against the function’s signature before it computes a result. Invalid function argument means one of those arguments passed the parse stage but failed the function’s own validation: a string where a number was needed, a netnum larger than the address space allows, a path that does not exist, or text that is not valid JSON.

Unlike a syntax error, the HCL here is well-formed. The problem is the value flowing into the function at evaluation time, which is why the error often appears only for certain inputs.

Common Causes

  • Wrong type passed to a function. Passing a string "3" where a number is expected, or a single object where a list is required.
  • cidrsubnet/cidrhost out of range. The newbits plus the base prefix exceed /32 (IPv4), or netnum/hostnum is larger than the subnet can hold.
  • file() on a missing path. The path is wrong, relative to the wrong directory, or the file is generated and not yet present.
  • jsondecode/yamldecode on malformed input. Trailing commas, unquoted keys, or a partial template render produce invalid documents.
  • Index out of range in element(). Calling element(list, n) where the list is empty (an empty list errors even though element wraps the index).
  • templatefile variable mismatch. The template references a variable not present in the vars map, or the map contains a value of an unexpected type.
  • regex/regexall pattern errors. An invalid regular expression, or a pattern with capture groups passed where a single match is expected.

How to Reproduce the Error

# network.tf — cidrsubnet netnum too large for the prefix extension
locals {
  vpc_cidr = "10.0.0.0/16"
  # 4 new bits => 16 subnets (0-15); 4096 is out of range
  bad_subnet = cidrsubnet(local.vpc_cidr, 4, 4096)
}

# data.tf — wrong type into a numeric function
variable "replicas" {
  type    = string
  default = "3"
}
locals {
  # max() expects numbers, not strings
  capacity = max(var.replicas, 5)
}
terraform plan
Error: Invalid function argument

  on network.tf line 5, in locals:
   5:   bad_subnet = cidrsubnet(local.vpc_cidr, 4, 4096)

Invalid value for "netnum" parameter: prefix extension of 4 does not
accommodate a subnet numbered 4096.

Diagnostic Commands

Find the function and argument Terraform rejected:

terraform plan 2>&1 | grep -A4 "Invalid function argument"

Test the failing expression interactively — terraform console evaluates real functions against your state and variables:

terraform console
> cidrsubnet("10.0.0.0/16", 4, 4096)
> type(var.replicas)
> can(jsondecode(file("${path.module}/config.json")))
Error: Invalid function argument
string
false

Validate a file exists and parses before Terraform touches it:

ls -l config.json
jq empty config.json    # exits non-zero on malformed JSON

Step-by-Step Resolution

1. Read the function signature. Match each argument to its expected type. cidrsubnet(prefix, newbits, netnum) needs a CIDR string, a number, and a number; templatefile(path, vars) needs a path and a map.

2. Convert the type explicitly. Wrap mistyped inputs in tostring(), tonumber(), tolist(), or toset():

locals {
  capacity = max(tonumber(var.replicas), 5)
}

3. Fix range errors. Ensure netnum < 2 ^ newbits and base_prefix + newbits <= 32:

# /16 + 4 newbits = /20 => valid netnum range is 0..15
local.subnet = cidrsubnet("10.0.0.0/16", 4, 3)

4. Guard fragile inputs with try() and can(). Provide a fallback instead of crashing:

locals {
  raw    = try(file("${path.module}/config.json"), "{}")
  config = try(jsondecode(local.raw), {})
}

can() returns a bool you can use in precondition blocks or validation:

variable "json_path" {
  type = string
  validation {
    condition     = can(jsondecode(file(var.json_path)))
    error_message = "json_path must point to a valid JSON file."
  }
}

5. Confirm in the console. Re-run the corrected expression in terraform console until it returns a value instead of an error, then terraform plan.

Prevention and Best Practices

  • Declare precise variable type constraints (number, list(string), map(object({...}))) so mistyped values fail fast at the variable boundary, not deep inside a function.
  • Wrap any function reading external data (file, jsondecode, yamldecode, templatefile) in try() with a sensible default.
  • Compute CIDR layouts once in locals and reference them, rather than recomputing cidrsubnet with magic numbers scattered across resources.
  • Lint JSON/YAML inputs in CI (jq empty, yamllint) before terraform plan runs.
  • Add validation blocks with can() to give callers a clear message instead of an opaque function error.
  • For triage, the free incident assistant can trace the rejected argument back to its source value. More patterns in the Terraform guides.
  • Invalid for_each argument — keys unknown at plan time, a related “value cannot be used here” failure.
  • Invalid index — accessing a list/map element that does not exist; often pairs with element() range issues.
  • Call to function failed — a function raised an error mid-evaluation (e.g. file() on a missing path) rather than rejecting an argument.
  • Invalid value for input variable — a value fails a validation block, frequently the can() guards you add to prevent this error.

Frequently Asked Questions

Why does cidrsubnet say my netnum is out of range when the number looks small? The valid range is 0 to 2^newbits - 1. With newbits = 4 you only get 16 subnets (0-15), so even netnum = 16 is out of range. Increase newbits to enlarge the address space, or lower netnum.

How do I stop file() from crashing when the file might not exist? Wrap it: try(file(path), ""). file() errors if the path is missing, so try() supplies a fallback. For optional config, combine with jsondecode: try(jsondecode(file(path)), {}).

What is the difference between try() and can()? try() returns the first expression that succeeds (a value or fallback). can() returns a boolean — true if the expression evaluates without error — and is meant for condition/validation blocks where you need a yes/no.

My templatefile fails with an invalid argument about a missing variable. Why? The template references ${something} that is not a key in the vars map you passed. Every interpolation in the template must have a matching key. Pass all referenced variables, or remove the unused interpolation from the template.

Can terraform console evaluate functions against my real variables? Yes. terraform console loads your variables, locals, data sources, and state, so cidrsubnet(var.vpc_cidr, 4, 3) evaluates exactly as it would during plan. It is the fastest way to reproduce and fix function argument errors.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.