GoogleContainerTools/skaffold · error

cannot marshal build artifacts: %w

Error message

cannot marshal build artifacts: %w

What it means

writeBuildArtifacts serializes the list of build artifacts (image names/digests/tags) to JSON for the Helm post-renderer. If json.Marshal of buildOutputs fails, this error is returned. JSON marshaling of plain structs rarely fails, so this usually indicates corrupted/unexpected artifact data.

Source

Thrown at pkg/skaffold/helm/util.go:37

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/graph"
	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/util"
)

// copy of cmd/skaffold/app/flags.BuildOutputs
type buildOutputs struct {
	Builds []graph.Artifact `json:"builds"`
}

func writeBuildArtifacts(builds []graph.Artifact) (string, func(), error) {
	buildOutput, err := json.Marshal(buildOutputs{builds})
	if err != nil {
		return "", nil, fmt.Errorf("cannot marshal build artifacts: %w", err)
	}

	f, err := os.CreateTemp("", "builds*.yaml")
	if err != nil {
		return "", nil, fmt.Errorf("cannot create temp file: %w", err)
	}
	if _, err := f.Write(buildOutput); err != nil {
		return "", nil, fmt.Errorf("cannot write to temp file: %w", err)
	}
	if err := f.Close(); err != nil {
		return "", nil, fmt.Errorf("cannot close temp file: %w", err)
	}
	return f.Name(), func() { os.Remove(f.Name()) }, nil
}

// SanitizeFilePath is used to sanitize filepaths that are provided to the `setFiles` flag
// helm `setFiles` doesn't work with the unescaped filepath separator (\) for Windows or if there are unescaped tabs and spaces in the directory names.
// So we escape all odd count occurrences of `\` for Windows, and wrap the entire string in quotes if it has spaces.

View on GitHub (pinned to a1189de023)

Solutions

  1. Inspect the wrapped marshal error to identify the offending field
  2. Remove or tag (json:"-") non-serializable fields on the artifact struct
  3. Ensure builds come from the standard build pipeline (standard graph.Artifact values)
  4. Rebuild Skaffold from unmodified sources to rule out local patches

Example fix

// before
type Artifact struct {
    Conn chan string `json:"conn"`
}
// after
type Artifact struct {
    Conn chan string `json:"-"`
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go caller pre-check: builds must be plain JSON-encodable
if b, err := json.Marshal(builds); err != nil { return fmt.Errorf("build artifacts not encodable: %w", err) }

Try / catch

skaffoldBinary, env, cleanup, err := helm.PrepareSkaffoldFilter(client, builds, flags)
if err != nil {
	if strings.Contains(err.Error(), "cannot marshal build artifacts") {
		log.Fatalf("non-serializable build artifact data: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: json.Marshal(buildOutputs{builds}) returns an error — practically only when a graph.Artifact contains a value JSON cannot encode (e.g. a channel, func, or cyclic custom type introduced in a custom build-artifact path).

Common situations: Patched or forked Skaffold with custom fields on graph.Artifact; plugin/embedding scenarios passing non-encodable values; otherwise virtually never seen in stock usage.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/5a1cec4382bbc276. Report an issue: GitHub.