larksuite/cli · error
exec provider value for id %q is not a string
Error message
exec provider value for id %q is not a string
What it means
The exec secret provider ran an external command and received a JSON value for the requested ref id, but that value is neither a plain string nor JSON-serializable data the resolver accepts. The library only treats string values (or values it can marshal to JSON) as valid secrets, so any other shape is rejected to avoid silently injecting a non-string secret into configuration. This guards the contract that resolved secrets are strings.
Source
Thrown at internal/binding/secret_resolve_exec.go:240
if resp.Values == nil {
return "", fmt.Errorf("exec provider response missing 'values'")
}
value, ok := resp.Values[refID]
if !ok {
return "", fmt.Errorf("exec provider response missing id %q", refID)
}
if str, ok := value.(string); ok {
return str, nil
}
if !jsonOnly {
data, err := json.Marshal(value)
if err != nil {
return "", fmt.Errorf("exec provider value for id %q is not JSON-serializable: %w", refID, err)
}
return string(data), nil
}
return "", fmt.Errorf("exec provider value for id %q is not a string", refID)
}
View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Change the exec command (or add a wrapper like jq -r) so it outputs a plain string for that ref id, e.g. `jq -r '.password.value'`.
- Update the SecretRef id to point at the JSON key that actually holds the string secret.
- If a structured value is intentional, ensure it is JSON-serializable at the provider so the marshal path in extractExecSecret succeeds instead of reaching the non-string branch.
Example fix
// before: exec command prints nested JSON
{"db": {"password": "s3cret"}}
// after: exec command prints the string value
jq -r '.db.password' | your-secret-command Defensive patterns
Strategy: validation
Validate before calling
out, err := runExecProvider(cmd) // your wrapper capturing stdout
var v any
if err := json.Unmarshal(out, &v); err == nil {
if _, ok := v.(string); !ok {
return fmt.Errorf("exec provider must output a string, got %T", v)
}
} Type guard
func isStringSecret(v any) bool { _, ok := v.(string); return ok } Try / catch
secret, err := resolveSecretRef(ctx, execRef)
if err != nil {
if strings.Contains(err.Error(), "is not a string") {
// inspect provider output shape and fix the ref id or command
}
return err
} Prevention
- Make exec provider commands emit exactly one plain string (use jq -r for JSON sources).
- Test each exec provider command's output shape alongside the binding config.
- Keep one ref id per output key and never point ids at nested objects.
When it happens
Trigger: Calling resolveSecretRef with a {source:"exec"} SecretRef where the exec provider's command outputs JSON whose value for ref.ID is an object, array, number, or boolean that fails the string/marshalable check in extractExecSecret (internal/binding/secret_resolve_exec.go:240).
Common situations: The external secret command (e.g. a vault CLI or script) returns a structured JSON object like {"password":{"value":"x"}} instead of a flat string for the requested id; a script was updated to emit nested config; or the ref id points at a non-string JSON field such as a number (port, TTL).
Related errors
- file provider JSON Pointer %q resolved to non-string value
- appSecret is missing or empty
- appSecret is empty string
- env variable %q referenced in openclaw.json is not set or em
- unsupported secret source %q
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/4509e33d9c1aea74.
Report an issue: GitHub.