larksuite/cli · error

appSecret must be a string or {source, provider?, id} object

Error message

appSecret must be a string or {source, provider?, id} object

What it means

This is the fallback rejection in SecretInput.UnmarshalJSON: the JSON value is neither a plain string (plaintext secret) nor a parseable {source, provider?, id} SecretRef object. It is the last guard so any structurally invalid appSecret input fails here.

Source

Thrown at internal/binding/types.go:114

		s.Ref = nil
		return nil
	}

	// Try SecretRef object
	var ref SecretRef
	if err := json.Unmarshal(data, &ref); err == nil {
		if !validSources[ref.Source] {
			return fmt.Errorf("SecretRef.source must be env|file|exec, got %q", ref.Source)
		}
		if ref.ID == "" {
			return fmt.Errorf("SecretRef.id must be non-empty")
		}
		s.Ref = &ref
		s.Plain = ""
		return nil
	}

	return fmt.Errorf("appSecret must be a string or {source, provider?, id} object")
}

// MarshalJSON serializes SecretInput back to JSON.
func (s SecretInput) MarshalJSON() ([]byte, error) {
	if s.Ref != nil {
		return json.Marshal(s.Ref)
	}
	return json.Marshal(s.Plain)
}

// SecretsConfig captures the secrets.providers registry from openclaw.json.
type SecretsConfig struct {
	Providers map[string]*ProviderConfig `json:"providers,omitempty"`
	Defaults  *ProviderDefaults          `json:"defaults,omitempty"`
}

// ProviderDefaults holds default provider aliases for each source type.
type ProviderDefaults struct {

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Pass a plain string for a literal secret
  2. Pass a complete {source, provider?, id} object with valid source (env|file|exec) and non-empty id
  3. Inspect the raw JSON type of the value; quote strings if a YAML/JSON parser coerced them

Example fix

// before
"appSecret": {"source": "env"}
// after
"appSecret": {"source": "env", "id": "FEISHU_APP_SECRET"}
Defensive patterns

Strategy: validation

Validate before calling

func validSecretInput(v any) error {
    if s, ok := v.(string); ok {
        if s == "" { return errors.New("appSecret empty") }
        return nil
    }
    m, ok := v.(map[string]any)
    if !ok { return errors.New("appSecret must be string or object") }
    src, _ := m["source"].(string)
    id, _ := m["id"].(string)
    if src != "env" && src != "file" && src != "exec" { return fmt.Errorf("bad source %q", src) }
    if id == "" { return errors.New("empty id") }
    return nil
}

Type guard

func isPlainStringOrRef(v any) bool {
    if _, ok := v.(string); ok { return true }
    m, ok := v.(map[string]any)
    return ok && m["source"] != nil && m["id"] != nil
}

Prevention

When it happens

Trigger: appSecret set to a JSON number, boolean, array, object missing required ref fields, or a malformed object that fails SecretRef unmarshal (e.g. {"source":123}).

Common situations: YAML/JSON type coercion surprises (unquoted secret values, keys parsed as numbers); partial ref objects; accidentally pasting a nested structure into a string field.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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