larksuite/cli · error
EmbeddedSkills() called more than once; a plugin owns at mos
Error message
EmbeddedSkills() called more than once; a plugin owns at most one SkillsOverlay
What it means
EmbeddedSkills() was called more than once on the same Builder. A plugin owns at most one SkillsOverlay, so a second call is a build error recorded in b.errs. This keeps overlay ownership unambiguous.
Source
Thrown at extension/platform/builder.go:171
// EmbeddedSkills contributes a SkillsOverlay (see SkillsOverlay) customizing
// the CLI's embedded skill content. It implies FailClosed: although skill
// content is not a command-enforcement boundary, the overlay is a distribution
// build-integrity declaration. Silently skipping it could republish host
// defaults that the distribution explicitly removed or replaced.
//
// Calling FailOpen before EmbeddedSkills is allowed; EmbeddedSkills overrides
// it to FailClosed, matching Restrict. Calling FailOpen afterward leaves an
// invalid final state that Build rejects. A later FailClosed restores a valid
// final state. A plugin owns at most one SkillsOverlay, so calling
// EmbeddedSkills more than once is a build error.
func (b *Builder) EmbeddedSkills(spec *SkillsOverlay) *Builder {
if spec == nil {
b.errs = append(b.errs, errors.New("EmbeddedSkills(nil): spec must not be nil"))
return b
}
if b.skillsOverlay != nil {
b.errs = append(b.errs, errors.New("EmbeddedSkills() called more than once; a plugin owns at most one SkillsOverlay"))
return b
}
b.caps.FailurePolicy = FailClosed
b.skillsOverlay = cloneSkillsOverlay(spec)
return b
}
// cloneSkillsOverlay snapshots the caller's spec so a later mutation of the
// same *SkillsOverlay cannot alter the staged copy. Selection and remap slices
// are copied; Overlay/Base are fs.FS handles retained by reference (an fs.FS is
// a read-only view, not caller-mutable state).
func cloneSkillsOverlay(spec *SkillsOverlay) *SkillsOverlay {
cp := *spec
cp.Allow = append([]string(nil), spec.Allow...)
cp.Remove = append([]string(nil), spec.Remove...)
cp.ReferenceRemaps = append([]SkillRefRemap(nil), spec.ReferenceRemaps...)
return &cp
}View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Merge all skills into a single *SkillsOverlay and call EmbeddedSkills once
- Create a fresh Builder if you genuinely need a second, independent plugin definition
- Call Build() and read the joined error to confirm which builder call sequence is at fault
Example fix
// before b.EmbeddedSkills(overlay1) b.EmbeddedSkills(overlay2) // error // after merged := mergeOverlays(overlay1, overlay2) b.EmbeddedSkills(merged)
Defensive patterns
Strategy: validation
Validate before calling
if overlayCount > 1 {
merged := mergeOverlays(overlays...)
builder.EmbeddedSkills(merged)
} else if overlayCount == 1 {
builder.EmbeddedSkills(overlays[0])
} Prevention
- Call EmbeddedSkills exactly once per Builder; merge overlays beforehand
- Wrap builder construction in a helper that centralizes the single overlay call
- Search builder chains for duplicate EmbeddedSkills calls during review
When it happens
Trigger: Calling builder.EmbeddedSkills(spec) twice (or in a loop) on the same Builder before Build().
Common situations: Merging skill sets from two sources by calling EmbeddedSkills for each; copy-pasted builder chains; a helper that unconditionally adds an overlay on top of one already added.
Related errors
- Restrict(nil): rule must not be nil
- EmbeddedSkills(nil): spec must not be nil
- Restrict() requires FailClosed; do not call FailOpen() after
- EmbeddedSkills() requires FailClosed; do not call FailOpen()
- Invalid column: {column!r}
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/f717a2503a559e18.
Report an issue: GitHub.