kubernetes/kops · error

variable is both an array and a scalar

Error message

variable %q is both an array and a scalar

What it means

TerraformWriter.AddOutputVariableArray appends a literal to a Terraform output treated as an array. If the same key was already registered as a scalar output (Value != nil), the same output name would have to be both a scalar and an array, which Terraform cannot express, so kOps errors. It enforces one shape per output variable.

Solutions

  1. Locate the renderer adding the scalar with the same key and rename/namespace one of the two outputs.
  2. If patching kOps, choose a distinct key for the array output (e.g. key + "_list").
  3. Check for duplicated resources in the cluster spec triggering both code paths.
  4. Update kOps; shape collisions are internal bugs fixed upstream.

Example fix

// before
AddOutputVariable("vpc_id", lit)
AddOutputVariableArray("vpc_id", lit2) // conflicts
// after
AddOutputVariable("vpc_id", lit)
AddOutputVariableArray("vpc_ids", lit2)
Defensive patterns

Strategy: try-catch

Try / catch

if err := w.AddOutputVariableArray(key, lit); err != nil {
    if strings.Contains(err.Error(), "both an array and a scalar") {
        return fmt.Errorf("output %q shape conflict; rename array variant", key)
    }
    return err
}

Prevention

When it happens

Trigger: A renderer calls AddOutputVariableArray(key, ...) for a key that another renderer previously registered via AddOutputVariable(key, scalar). The mixed registration happens when two different features emit outputs with the same name but different shapes.

Common situations: kOps internal naming collision between a scalar output and an array output with identical names; patched/extended kOps adding an array output over an existing scalar name; duplicated render logic registering both forms.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/61532852471baae4. Report an issue: GitHub.

Appendix: source

Thrown at upup/pkg/fi/cloudup/terraformWriter/writer.go:202

		return fmt.Errorf("duplicate variable: %q", key)
	}
	t.outputs[key] = v

	return nil
}

func (t *TerraformWriter) AddOutputVariableArray(key string, literal *Literal) error {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	if t.outputs[key] == nil {
		v := &terraformOutputVariable{
			Key: key,
		}
		t.outputs[key] = v
	}
	if t.outputs[key].Value != nil {
		return fmt.Errorf("variable %q is both an array and a scalar", key)
	}

	t.outputs[key].ValueArray = append(t.outputs[key].ValueArray, literal)

	return nil
}

func (t *TerraformWriter) GetDataSourcesByType() (map[string]map[string]interface{}, error) {
	dataSourcesByType := make(map[string]map[string]interface{})

	for _, dataSource := range t.dataSources {
		dataSources := dataSourcesByType[dataSource.DataType]
		if dataSources == nil {
			dataSources = make(map[string]interface{})
			dataSourcesByType[dataSource.DataType] = dataSources
		}

		tfName := sanitizeName(dataSource.DataName)

View on GitHub (pinned to 4c8573c808)