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

  1. Merge all skills into a single *SkillsOverlay and call EmbeddedSkills once
  2. Create a fresh Builder if you genuinely need a second, independent plugin definition
  3. 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

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


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