larksuite/cli · error
Input.Relations[%d] duplicates an earlier relation
Error message
Input.Relations[%d] duplicates an earlier relation
What it means
Thrown when two relations in Input.Relations are structurally identical — the compiler JSON-marshals each definition and rejects a relation whose key was already seen. Duplicate relations add no information and would compile to ambiguous behavior.
Source
Thrown at shortcuts/common/typed_compile_contract.go:51
return nil, fmt.Errorf("Input.Relations[%d].Stage %q is invalid", i, definition.Stage)
}
compiled := compiledRelation{kind: definition.Kind, presence: definition.Presence, stage: definition.Stage}
local := make(map[string]struct{})
for _, name := range definition.Params {
field, ok := fieldByName[name]
if !ok {
return nil, fmt.Errorf("Input.Relations[%d] references unknown param --%s", i, name)
}
if _, duplicate := local[name]; duplicate {
return nil, fmt.Errorf("Input.Relations[%d] repeats param --%s", i, name)
}
local[name] = struct{}{}
compiled.fields = append(compiled.fields, field)
}
keyBytes, _ := json.Marshal(definition)
key := string(keyBytes)
if _, duplicate := seen[key]; duplicate {
return nil, fmt.Errorf("Input.Relations[%d] duplicates an earlier relation", i)
}
seen[key] = struct{}{}
result = append(result, compiled)
}
return result, nil
}
func compileAuthorization(definition typedAuthorizationDefinition, fields []compiledInputField, fieldByName map[string]int) error {
for identity, authorization := range definition.Identities {
for i, conditional := range authorization.ConditionalScopes {
path := fmt.Sprintf("Authorization.%s.ConditionalScopes[%d]", identity, i)
if len(conditional.Params) > 0 && conditional.When == "" {
return fmt.Errorf("%s.Params requires agent-readable When text", path)
}
seen := make(map[string]struct{}, len(conditional.Params))
for j, param := range conditional.Params {
if param == "" || param != strings.TrimSpace(param) {
return fmt.Errorf("%s.Params[%d] must be a non-blank trimmed param", path, j)View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Delete the redundant relation entry
- Differentiate the relations (distinct Kind/Presence/Stage/Params) if both were intentional
Example fix
// before
Relations: []Relation{
{Kind: "lookup", Params: []string{"--chat-id"}},
{Kind: "lookup", Params: []string{"--chat-id"}},
}
// after
Relations: []Relation{
{Kind: "lookup", Params: []string{"--chat-id"}},
} Defensive patterns
Strategy: validation
Validate before calling
keys := map[string]struct{}{}
for _, rel := range def.Input.Relations {
b, _ := json.Marshal(rel)
if _, dup := keys[string(b)]; dup { return fmt.Errorf("duplicate relation") }
keys[string(b)] = struct{}{}
} Try / catch
if err := common.CompileTypedDefinition(def); err != nil {
return fmt.Errorf("duplicate relation: %w", err)
} Prevention
- Copy-paste relation blocks only with an immediate edit of Kind or Params
- Generate relations from a map to avoid accidental repetition
- Add a compile-all-definitions test
When it happens
Trigger: Input.Relations contains two Relation entries with identical Kind, Presence, Stage, and Params, e.g. from an accidental copy-paste.
Common situations: Copy-pasting a relation block and forgetting to edit it; merging definitions from two shortcuts; programmatic generation appending the same relation twice.
Related errors
- Input.Relations[%d] repeats param --%s
- Input.Relations[%d] references unknown param --%s
- Invalid column: {column!r}
- Invalid column index: {index}
- anchor outside sheet: {position!r}
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/9ea5f3becbe13023.
Report an issue: GitHub.