kubesphere/kubesphere · error

failed to build merge specs: %v

Error message

failed to build merge specs: %v

What it means

v2.MergeSpecCache builds a single merged OpenAPI v2 spec from all cached APIService specs using MergeSpecsIgnorePathConflictRenamingDefinitionsAndParameters. This error wraps any failure of that merge (including the GVK extension shape errors 60-63) with a 'failed to build merge specs' prefix.

Source

Thrown at kube/pkg/openapi/v2/services.go:86

func (s *OpenApiV2Services) RemoveApiService(apiServiceName string) {
	s.openApiAggregatorService.RemoveApiService(apiServiceName)
	delete(s.openApiSpecCache, apiServiceName)
}

func (s *OpenApiV2Services) MergeSpecCache() (*spec.Swagger, error) {
	var merged *spec.Swagger
	for i := range s.openApiSpecCache {
		if cacheValue, ok := s.openApiSpecCache[i]; ok {
			cacheSpec := cacheValue.Load()
			if merged == nil {
				merged = &spec.Swagger{}
				*merged = *cacheSpec
				merged.Paths = nil
				merged.Definitions = nil
				merged.Parameters = nil
			}
			if err := merge.MergeSpecsIgnorePathConflictRenamingDefinitionsAndParameters(merged, cacheSpec); err != nil {
				return nil, fmt.Errorf("failed to build merge specs: %v", err)
			}
		}
	}
	return merged, nil
}

func (s *OpenApiV2Services) RegisterOpenAPIVersionedService(servePath string, handler openapi.PathHandler) {
	handler.Handle(servePath, gziphandler.GzipHandler(http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {

			result, err := s.MergeSpecCache()
			if err != nil {
				klog.Errorf("Error in OpenAPI handler: %s", err)
				// only return a 503 if we have no older cache data to serve
				if result == nil {
					w.WriteHeader(http.StatusServiceUnavailable)
					return
				}

View on GitHub (pinned to 04a29b5c60)

Solutions

  1. Identify the inner (wrapped) error to find the offending cached spec and fix or delete that cache entry
  2. Clear the spec cache so all specs are re-downloaded and re-merged
  3. Fix the malformed extension in the offending spec (array of {group,kind,version} objects)
  4. Validate each cached spec parses and has well-formed extensions before merging
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-validate cached specs before merging
for name, spec := range cache {
    if !validGVKExtension(spec) {
        invalidateCacheEntry(name)
    }
}

Type guard

func validGVKExtension(spec map[string]interface{}) bool {
    ext, ok := spec["x-kubernetes-group-version-kind"].([]interface{})
    if !ok { return false }
    for _, x := range ext {
        if _, ok := x.(map[string]interface{}); !ok { return false }
    }
    return true
}

Try / catch

merged, err := v2.MergeSpecCache(cache)
if err != nil {
    if strings.Contains(err.Error(), "failed to build merge specs") {
        // identify and purge the offending cache entry, then rebuild
        clearCache()
        return v2.MergeSpecCache(reloadCache())
    }
    return err
}

Prevention

When it happens

Trigger: During MergeSpecCache iteration over cached specs, merging a subsequent cached spec into the accumulated merged spec fails — typically because a cached spec has a malformed x-kubernetes-group-version-kind extension or otherwise incompatible spec content.

Common situations: One corrupted or hand-written cached spec poisons the whole merge; specs from differently-versioned generators conflict; empty/invalid cached documents left after failed downloads.

Related errors


AI-assisted analysis of kubesphere/kubesphere@04a29b5c60 (2026-09-03). Data as JSON: /api/errors/5c73f81730d0e4f2. Report an issue: GitHub.