router-for-me/CLIProxyAPI · error

vertex credential: merge metadata failed: %w

Error message

vertex credential: merge metadata failed: %w

What it means

Returned by VertexCredentialStorage.SaveTokenToFile when misc.MergeMetadata fails to combine the storage struct with its Metadata map before JSON encoding (vertex_credentials.go:64-67). Like the Kimi equivalent, this is a JSON-marshal failure over the metadata — the VertexCredentialStorage or its Metadata contains values Go's encoder cannot serialize.

Source

Thrown at internal/auth/vertex/vertex_credentials.go:66

// It ensures the parent directory exists and logs the operation for transparency.
func (s *VertexCredentialStorage) SaveTokenToFile(authFilePath string) error {
	misc.LogSavingCredentials(authFilePath)
	if s == nil {
		return fmt.Errorf("vertex credential: storage is nil")
	}
	if s.ServiceAccount == nil {
		return fmt.Errorf("vertex credential: service account content is empty")
	}
	// Ensure we tag the file with the provider type.
	s.Type = "vertex"

	if err := os.MkdirAll(filepath.Dir(authFilePath), 0o700); err != nil {
		return fmt.Errorf("vertex credential: create directory failed: %w", err)
	}

	data, errMerge := misc.MergeMetadata(s, s.Metadata)
	if errMerge != nil {
		return fmt.Errorf("vertex credential: merge metadata failed: %w", errMerge)
	}

	f, err := os.Create(authFilePath)
	if err != nil {
		return fmt.Errorf("vertex credential: create file failed: %w", err)
	}
	defer func() {
		if errClose := f.Close(); errClose != nil {
			log.Errorf("vertex credential: failed to close file: %v", errClose)
		}
	}()
	enc := json.NewEncoder(f)
	enc.SetIndent("", "  ")
	if err = enc.Encode(data); err != nil {
		return fmt.Errorf("vertex credential: encode failed: %w", err)
	}
	return nil
}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Restrict Metadata entries to JSON-native types; stringify anything exotic before assignment
  2. Inspect the wrapped cause in the error chain to find the offending key
  3. If ServiceAccount was built programmatically, ensure it came from json.Unmarshal rather than hand-built Go values

Example fix

// before
s.Metadata = map[string]any{"stop": make(chan struct{})}
// after
s.Metadata = map[string]any{"stop": "signal-name"}
Defensive patterns

Strategy: validation

Validate before calling

if _, err := json.Marshal(s.Metadata); err != nil {
    return fmt.Errorf("vertex metadata not serializable: %w", err)
}

Try / catch

if err := s.SaveTokenToFile(path); err != nil && strings.Contains(err.Error(), "merge metadata failed") {
    s.Metadata = nil
    err = s.SaveTokenToFile(path)
}

Prevention

When it happens

Trigger: Metadata map holding channels, funcs, or cyclic references; a ServiceAccount map containing unserializable Go values (rare, since it normally comes from JSON); unsupported numeric types like NaN.

Common situations: SDK embedders attaching runtime objects as metadata; NaN/Inf floats injected by custom accounting code; effectively unreachable via plain config-driven usage.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/154cde6698daa52d. Report an issue: GitHub.