larksuite/cli · error
suite skills sync failed: %s
Error message
suite skills sync failed: %s
What it means
SyncSkills aggregates the per-source failure reasons collected while trying every configured skills source. When the target layout is LayoutSuite and no source succeeded, it returns a SyncResult with Action="failed" and this error joining all reasons with "; " (the multiline breakdown is in SyncResult.Detail). It is a top-level aggregate, not a root cause: read the embedded per-source reasons to find the underlying index-fetch or sync-layout failure.
Source
Thrown at internal/skillscheck/sync.go:340
LocalSkills: localOfficial,
PreviousState: previous,
StateReadable: readable,
Force: opts.Force,
})
fallbackPlan = &plan
if syncErr := syncLayout(opts.Runner, source, targetLayout, plan, installed); syncErr != nil {
reasons = append(reasons, source+": "+syncErr.Error())
continue
}
return finishSync(opts, targetLayout, plan, "", "", false)
}
if targetLayout == LayoutSuite {
return &SyncResult{
Action: "failed",
Layout: targetLayout,
Err: fmt.Errorf("suite skills sync failed: %s", strings.Join(reasons, "; ")),
Detail: strings.Join(reasons, "\n"),
Force: opts.Force,
}
}
return fallbackSeparate(opts, previous, readable, localOfficial, installed, fallbackPlan, reasons)
}
func fetchOfficialSkills(runner SkillsRunner, source string) ([]string, error) {
result := runner.FetchSkillsIndex(source)
if result == nil || result.Err != nil {
return nil, fmt.Errorf("index request failed: %s", resultDetail(result))
}
official, err := ParseOfficialSkillsIndexJSON(result.Stdout.String())
if err != nil {
return nil, fmt.Errorf("invalid v0.2 index: %w", err)
}
if len(official) == 0 {View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Read SyncResult.Detail (or the joined Err message) for the per-source reason and fix the underlying failure for each source
- Verify network connectivity and that the skills index source URL is reachable (curl the index URL)
- Check that the remote publishes a valid non-empty v0.2 skills index (see errors 426-428 for specifics)
- Inspect the SkillsRunner implementation and its SkillsSources() configuration for misconfiguration
- Re-run with opts.Force or after fixing state if local state corruption contributes
Example fix
// before: opaque aggregate
res := SyncSkills(opts)
if res.Action == "failed" { log.Println(res.Err) }
// after: surface per-source breakdown
res := SyncSkills(opts)
if res.Action == "failed" {
for _, line := range strings.Split(res.Detail, "\n") {
log.Printf("source failure: %s", line)
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if opts.Runner == nil { return errors.New("runner must be set before SyncSkills") }
for _, s := range opts.Runner.SkillsSources() {
if !isReachable(s) { log.Printf("source unreachable before sync: %s", s) }
} Try / catch
res := SyncSkills(opts)
if res.Action == "failed" && res.Err != nil {
var syncErr = res.Err
// per-source breakdown is in res.Detail
for _, reason := range strings.Split(res.Detail, "\n") {
log.Printf("sync source failed: %s", reason)
}
if strings.Contains(syncErr.Error(), "suite skills sync failed") {
// network/index problem, safe to retry after connectivity check
}
} Prevention
- Check network/proxy reachability of all configured sources before syncing
- Keep SkillsSources() configured with a healthy production index URL
- Inspect SyncResult.Detail, not just Err, when a suite sync fails
- Keep the skills CLI updated so index schema and sync code stay compatible
When it happens
Trigger: Calling SyncSkills with target layout LayoutSuite when every source in Runner.SkillsSources() fails either fetchOfficialSkills (index request failed / invalid index / empty index) or syncLayout.
Common situations: No network access or the skills index host is unreachable; the configured source URL is wrong; the remote serves a non-v0.2 or malformed index JSON; the runner's skills command is broken so syncLayout fails everywhere; a proxy/firewall blocks the index endpoint.
Related errors
- annotated_csv did not parse into the rows the server reporte
- +csv-get truncated the scan range at {actual_range}; narrow
- --max-sheets must be at least 1
- Device authorization failed: read body: %v
- failed to read response body in security transport: %w
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/b49bfbc566e7ba4c.
Report an issue: GitHub.