larksuite/cli · error
Args field %s is unexported but declares Typed input tags
Error message
Args field %s is unexported but declares Typed input tags
What it means
collectArgFields walks the Args struct via reflection and skips unexported fields, but an unexported field that declares Typed input tags (flag, arg, schema, cli, doc, json) indicates a bug — those tags could never take effect since the field is not settable via reflection. The compiler fails fast with this error instead of silently ignoring the tags.
Source
Thrown at shortcuts/common/typed_compile_args.go:85
return nil, nil, fmt.Errorf("Args field %s (--%s): alias --%s duplicates %s", field.goName, field.name, alias.Name, previous)
}
allNames[alias.Name] = "alias of --" + field.name
}
}
if len(supplements) > 0 {
for name := range supplements {
return nil, nil, fmt.Errorf("Input.Fields references unknown flag --%s", name)
}
}
return fields, fieldByName, nil
}
func collectArgFields(t reflect.Type, parentIndex []int, insideInline bool, out *[]compiledInputField, seenGo map[string]struct{}, supplements map[string]typedInputField) error {
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if !field.IsExported() {
if hasAnyTag(field, "flag", "arg", "schema", "cli", "doc", "json") {
return fmt.Errorf("Args field %s is unexported but declares Typed input tags", field.Name)
}
continue
}
flagName, hasFlag := field.Tag.Lookup("flag")
argMode, hasArg := field.Tag.Lookup("arg")
if hasFlag == hasArg {
return fmt.Errorf("Args field %s must declare exactly one of flag or arg", field.Name)
}
if _, duplicate := seenGo[field.Name]; duplicate {
return fmt.Errorf("Args field %s is duplicated through inline expansion", field.Name)
}
seenGo[field.Name] = struct{}{}
index := append(append([]int(nil), parentIndex...), i)
if hasArg {
switch argMode {
case "local":
if insideInline {
return fmt.Errorf("Args field %s: arg:\"local\" is not allowed inside inline", field.Name)View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Export the field (capitalize its name) if it should be a CLI flag/arg.
- Remove the input tags from the unexported field if it is internal state.
- Move the value out of Args if it should not be exposed on the command line.
Example fix
// before
type Args struct {
userID string `flag:"user-id" doc:"user id"`
}
// after
type Args struct {
// Lark user ID to assign the task.
UserID string `flag:"user-id"`
} Defensive patterns
Strategy: validation
Validate before calling
func noTaggedUnexported(t reflect.Type) error {
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if !f.IsExported() {
for _, tag := range []string{"flag","arg","schema","cli","doc","json"} {
if _, ok := f.Tag.Lookup(tag); ok {
return fmt.Errorf("unexported field %s has %q tag", f.Name, tag)
}
}
}
}
return nil
} Prevention
- Only exported fields may carry input tags — keep Args fields capitalized.
- Strip tags when making a field private, or drop it from Args.
- Run go vet / struct-tag linters over Args structs.
When it happens
Trigger: Declaring a lowercase (unexported) struct field with any of the tags flag, arg, schema, cli, doc, or json in the Args struct and compiling the definition.
Common situations: Accidentally lowercasing a field name while adding tags; copying an internal helper field from another struct along with its tags; generating structs where a field was made private but tags remained.
Related errors
- %v overflows %s
- %T is not assignable to %s
- Args must be a non-pointer struct, got %s
- Args field %s flag --%s duplicates %s
- Provided type has no Value field
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/e0a5b49f135e9812.
Report an issue: GitHub.