larksuite/cli · error

EmbeddedSkills(nil): spec must not be nil

Error message

EmbeddedSkills(nil): spec must not be nil

What it means

EmbeddedSkills() was called with a nil *SkillsOverlay. The builder rejects nil specs up front and records the error for Build(). A nil overlay carries no skills to embed, so it is treated as a programming mistake rather than a no-op.

Source

Thrown at extension/platform/builder.go:167

	cp.Identities = append([]Identity(nil), rule.Identities...)
	b.rules = append(b.rules, &cp)
	return b
}

// 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...)

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Build a valid *SkillsOverlay (with at least the required skills entries) before calling EmbeddedSkills
  2. Skip the EmbeddedSkills call if the plugin has no overlay to ship
  3. Call Build() to surface all accumulated builder errors and fix the nil input

Example fix

// before
var overlay *SkillsOverlay
b.EmbeddedSkills(overlay)

// after
if overlay != nil {
    b = b.EmbeddedSkills(overlay)
}
Defensive patterns

Strategy: validation

Validate before calling

if spec == nil {
    return nil, fmt.Errorf("cannot build plugin: skills overlay is nil")
}
b := builder.EmbeddedSkills(spec)

Type guard

func hasOverlay(s *SkillsOverlay) bool { return s != nil }

Prevention

When it happens

Trigger: Calling builder.EmbeddedSkills(nil), e.g. when the overlay is produced conditionally or loaded from a source that returned nil on failure.

Common situations: Skill overlay file missing or failed to parse so the loader returned nil; a struct field of type *SkillsOverlay left unset; refactor that made overlay construction conditional.

Related errors


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