Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

VariableBlock rejects optional object attribute defaults

Open
#98 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
68/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
go
Domain
backend

Research direction

Start in variable_test.go with the provided variableSuite reproduction, then trace VariableBlock.parseVariableType and the paths that process CLI files, environment variables, prompts, and variable defaults. Compare the current typeexpr.Type call with TypeConstraintWithDefaults and verify that nested defaults are applied before conversion. Done means the focused test passes and partial objects receive the documented nested defaults without changing existing conversion or validation behavior.

Written by the indexing model from the issue text.

Description

Summary

VariableBlock cannot use optional object attributes with defaults in a variable type, even though HCL supports this syntax for type constraints:

variable "provider" {
  type = object({
    type     = optional(string, "openai")
    endpoint = string
    retry = optional(object({
      attempts = optional(number, 3)
    }), {})
  })

  default = {
    endpoint = "https://example.test/v1"
  }
}

Planning fails while parsing the variable type. This prevents callers from passing a partial object and relying on field-level defaults, which is particularly useful for provider-style configuration.

Reproduction test

The following test can be added directly to variable_test.go in the existing variableSuite:

func (s *variableSuite) TestExecuteBeforePlan_ObjectOptionalAttributeDefaults() {
	s.dummyFsWithFiles(map[string]string{
		"test.hcl": `variable "provider" {
  type = object({
    type     = optional(string, "openai")
    endpoint = string
    retry = optional(object({
      attempts = optional(number, 3)
    }), {})
  })
  default = {
    endpoint = "https://example.test/v1"
  }
}`,
	})

	config, err := BuildDummyConfig("/", "", nil, nil)
	require.NoError(s.T(), err)
	variable := Blocks[*VariableBlock](config)[0]
	s.Equal(cty.ObjectVal(map[string]cty.Value{
		"type":     cty.StringVal("openai"),
		"endpoint": cty.StringVal("https://example.test/v1"),
		"retry": cty.ObjectVal(map[string]cty.Value{
			"attempts": cty.NumberIntVal(3),
		}),
	}), *variable.variableValue)
}

Run:

go test ./... -run 'TestVariableSuite/TestExecuteBeforePlan_ObjectOptionalAttributeDefaults' -count=1

Actual result:

--- FAIL: TestVariableSuite/TestExecuteBeforePlan_ObjectOptionalAttributeDefaults
    Received unexpected error:
        test.hcl:3,16-24: Invalid type specification;
        Optional attribute modifier is only for type constraints, not for exact types.,
        and 2 other diagnostic(s)

Configurations containing optional(type, default) can also report:

Optional attribute modifier expects only one argument: the attribute type.

Expected behavior

BuildDummyConfig should succeed. The evaluated var.provider value should be:

{
  type     = "openai"
  endpoint = "https://example.test/v1"
  retry = {
    attempts = 3
  }
}

A top-level variable default is not an equivalent workaround: once a caller supplies a partial object, it does not merge defaults into omitted nested attributes.

Root cause

On current main, VariableBlock.parseVariableType uses:

t, diag := typeexpr.Type(typeAttr.Expr)

typeexpr.Type parses an exact type. HCL exposes typeexpr.TypeConstraintWithDefaults specifically for variable-style type constraints with optional(type, default).

TypeConstraintWithDefaults returns both the target cty.Type and a *typeexpr.Defaults tree. The defaults must be retained by VariableBlock and applied to the selected input value before conversion. Merely changing the parser without calling Defaults.Apply would accept the syntax but still lose the intended defaults.

Suggested direction

  1. Parse variable types with typeexpr.TypeConstraintWithDefaults.
  2. Store the returned defaults on VariableBlock.
  3. Apply them to values from CLI files, environment variables, prompts, and the variable-level default before convert.Convert.
  4. Preserve the current type conversion and validation behavior.
  5. Add coverage for nested optional object defaults and partial caller-supplied objects.

Observed against Azure/golden main at 6e9a3fc2760e6f8440e4dfe8d0b1360886588bdd.

Dominant language
Go
Stars
3
Forks
13
PR merge metrics
No merged PRs in 30d

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Azure/golden

All issues in Azure/golden

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.