larksuite/cli · error

L2: required key %q not found in properties

Error message

L2: required key %q not found in properties

What it means

lintEnvelope's L2 validation enforces that every key listed in inputSchema.required actually exists in inputSchema.properties. This error names the required key that has no corresponding property definition. Top-level required keys must exist in top-level properties, even though format/min-max checks walk the whole property tree.

Source

Thrown at internal/schema/lint.go:78

	}

	// L1: validate every Property type recursively
	if env.InputSchema != nil && env.InputSchema.Properties != nil {
		validatePropertyTypes(env.InputSchema.Properties, &errs)
	}
	if env.OutputSchema != nil && env.OutputSchema.Properties != nil {
		validatePropertyTypes(env.OutputSchema.Properties, &errs)
	}

	// ---- L2: type-level consistency ----
	if env.InputSchema != nil && env.InputSchema.Properties != nil {
		// Walk the whole property tree so format/min-max checks reach leaf
		// fields nested under the params/data wrapper.
		walkForL2(env.InputSchema.Properties, &errs)
		// Top-level required keys must exist in top-level properties.
		for _, r := range env.InputSchema.Required {
			if _, ok := env.InputSchema.Properties.Map[r]; !ok {
				errs = append(errs, fmt.Errorf("L2: required key %q not found in properties", r))
			}
		}
	}

	// ---- L3: cross-field self-consistency ----
	dangerExpected := env.Meta.Risk == core.RiskWrite || env.Meta.Risk == core.RiskHighRiskWrite
	if env.Meta.Danger != dangerExpected {
		errs = append(errs, fmt.Errorf("L3: _meta.danger=%v inconsistent with risk=%q", env.Meta.Danger, env.Meta.Risk))
	}

	// `yes` lives at inputSchema.properties.yes (sibling of params/data),
	// injected only for risk == RiskHighRiskWrite.
	hasYes := false
	if env.InputSchema != nil && env.InputSchema.Properties != nil {
		_, hasYes = env.InputSchema.Properties.Map["yes"]
	}
	wantYes := env.Meta.Risk == core.RiskHighRiskWrite
	if hasYes != wantYes {

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Add a property definition for the missing key in inputSchema.properties.
  2. Fix spelling mismatches between the required array and the properties map.
  3. If the key is genuinely nested, remove it from top-level required and validate it at its actual level.
  4. For RiskHighRiskWrite tools, ensure the "yes" property is declared alongside being required.

Example fix

// before
"required": ["params", "confirm"], "properties": { "params": {} }
// after
"required": ["params", "confirm"], "properties": { "params": {}, "confirm": { "type": "boolean" } }
Defensive patterns

Strategy: validation

Validate before calling

func requiredKeysDefined(env map[string]any) []string {
	s, _ := env["inputSchema"].(map[string]any)
	req, _ := s["required"].([]any)
	props, _ := s["properties"].(map[string]any)
	var missing []string
	for _, r := range req {
		k, _ := r.(string)
		if _, ok := props[k]; !ok { missing = append(missing, k) }
	}
	return missing
}

Type guard

func allRequiredDefined(schema *InputSchema) bool {
	for _, r := range schema.Required {
		if _, ok := schema.Properties.Map[r]; !ok { return false }
	}
	return true
}

Try / catch

errs := lintEnvelope(env)
for _, e := range errs {
	if strings.Contains(e.Error(), "required key") {
		// add the named key to inputSchema.properties or fix the spelling
	}
}

Prevention

When it happens

Trigger: Linting an envelope where inputSchema.required contains e.g. ["params", "yes"] but properties only defines {"params": ...} — the listed key is missing, misspelled, or only nested deeper in the tree.

Common situations: Adding a required flag without defining its property; renaming a property but not the required array; expecting nested keys to satisfy a top-level required entry; forgetting the "yes" property for high-risk-write tools.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/d226213009f2d2cf. Report an issue: GitHub.