mikefarah/yq · error

system operator: command must be a string scalar

Error message

system operator: command must be a string scalar

What it means

The `system` operator's resolved command must be a string scalar. resolveCommandNode throws this when the first node returned by the command expression is not a scalar, or is a scalar whose (effective) tag is not !!str — e.g. a number, boolean, map, or sequence.

Source

Thrown at pkg/yqlib/operator_system.go:53

	if argsNode.Tag == "!!null" {
		return nil, nil
	}
	if argsNode.Kind != ScalarNode {
		return nil, fmt.Errorf("system operator: args must be a non-null scalar or sequence of non-null scalars; got kind=%v tag=%v", argsNode.Kind, argsNode.Tag)
	}
	return []string{argsNode.Value}, nil
}

func resolveCommandNode(commandNodes Context) (string, error) {
	if commandNodes.MatchingNodes.Front() == nil {
		return "", fmt.Errorf("system operator: command expression returned no results")
	}
	if commandNodes.MatchingNodes.Len() > 1 {
		log.Debugf("system operator: command expression returned %d results, using first", commandNodes.MatchingNodes.Len())
	}
	cmdNode := commandNodes.MatchingNodes.Front().Value.(*CandidateNode)
	if cmdNode.Kind != ScalarNode || cmdNode.guessTagFromCustomType() != "!!str" {
		return "", fmt.Errorf("system operator: command must be a string scalar")
	}
	if cmdNode.Value == "" {
		return "", fmt.Errorf("system operator: command must be a non-empty string")
	}
	return cmdNode.Value, nil
}

func systemOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
	if !ConfiguredSecurityPreferences.EnableSystemOps {
		return Context{}, fmt.Errorf("system operations are disabled, use --security-enable-system-operator to enable")
	}

	// determine at parse time whether we have (command; args) or just (command)
	hasArgs := expressionNode.RHS.Operation.OperationType == blockOpType

	var results = list.New()

	for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {

View on GitHub (pinned to 8b5af0694b)

Solutions

  1. Point the command expression at a string field, e.g. `system(.commandString)`
  2. Coerce non-string scalars to a string: `system(.port | tostring)`
  3. Check the document tags with `yq 'path | tag'` to confirm the node is !!str

Example fix

// before: .port is !!int
system(.port)
// after: convert to string scalar
system(.port | tostring)
Defensive patterns

Strategy: type-guard

Validate before calling

// only pass string scalars as the command
system(select(tag == "!!str") | .cmd)

Type guard

// guard in expression form
def isStrScalar: tag == "!!str";
.cmd | select(isStrScalar) | system(.)

Try / catch

// Go API usage
if err != nil && strings.Contains(err.Error(), "command must be a string scalar") {
    // coerce or report: the selected node was not !!str
}

Prevention

When it happens

Trigger: `system(.port)` where .port is `!!int`, `system(.enabled)` where the value is `!!bool`, or `system(.config)` where .config is a map. Also custom-typed scalars whose tag does not resolve to !!str.

Common situations: Pointing the command expression at a numeric/boolean field by mistake, or reading a command from data loaded where the tag is a custom type rather than a plain string.

Related errors


AI-assisted analysis of mikefarah/yq@8b5af0694b (2026-09-05). Data as JSON: /api/errors/9b51e92229fb9993. Report an issue: GitHub.