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
- Inspect the wrapped marshal error to identify the offending field
- Remove or tag (json:"-") non-serializable fields on the artifact struct
- Ensure builds come from the standard build pipeline (standard graph.Artifact values)
- 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
- Keep graph.Artifact values plain and JSON-serializable
- Avoid forking/patching Artifact structs with exotic fields
- Tag non-serializable fields json:"-" if extending structs
- Round-trip builds through json.Marshal in tests to catch regressions
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
- strings.Join(errMsgs, "\n") (joined helm cleanup error messa
- Failed to install Helm plugin
- Failed to render release
- skaffold apply called with conflicting namespaces set via sk
- loading artifacts: %w
AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05).
Data as JSON: /api/errors/5a1cec4382bbc276.
Report an issue: GitHub.