kopia/kopia · error
marshal token
Error message
marshal token
What it means
EncodeToken builds a token info struct (storage connection info + password) and serializes it with json.Marshal. If Go's JSON encoder fails to marshal that struct, the error is wrapped with "marshal token" and returned as part of the encoded token string failure. In practice this almost never fires for plain structs, so it usually indicates an unusual value type embedded in the token info that json.Marshal cannot represent (e.g. a channel, func, or cyclic value) or an out-of-memory/rare encoder failure.
Solutions
- Inspect the wrapped cause with errors.Cause / %v to identify which value failed to marshal.
- Ensure the ConnectionInfo and all nested fields contain only JSON-serializable types (strings, numbers, maps, slices).
- Upgrade kopia if using a custom storage backend; check for known marshal bugs in your backend plugin.
Example fix
// before ci.Config = someFuncField // func type - not JSON marshalable v, err := json.Marshal(ti) // after ci.Config = serializedConfigString // JSON-safe representation v, err := json.Marshal(ti)
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure token info fields are JSON-serializable before encoding
if err := json.Valid([]byte("{}")); err != nil { /* env issue */ }
// or attempt a dry-run marshal of the ConnectionInfo config
if _, err := json.Marshal(ci.Config); err != nil {
return fmt.Errorf("connection info not JSON-serializable: %w", err)
} Type guard
func isJSONSerializable(v any) bool {
_, err := json.Marshal(v)
return err == nil
} Try / catch
token, err := repo.EncodeToken(ci, password)
if err != nil {
var jsonErr *json.UnsupportedTypeError
if errors.As(err, &jsonErr) {
// handle non-serializable field: jsonErr.Type
}
return fmt.Errorf("token encoding failed: %w", err)
} Prevention
- Keep ConnectionInfo config limited to JSON-safe types (string, numbers, maps, slices).
- Never embed funcs, channels, or cyclic references in storage backend config.
- Test custom storage backends with a round-trip EncodeToken/DecodeToken unit test.
When it happens
Trigger: Calling repo.EncodeToken(ci, password) when the underlying TokenInfo struct (containing storage.ConnectionInfo and password) cannot be JSON-marshaled by encoding/json.
Common situations: Custom storage backends or wrapper types that inject non-JSON-serializable values into ConnectionInfo fields; corrupted builds or exotic embedded types; extremely rare encoder failures. Developers rarely see this in normal kopia 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
- can't marshal blobCfgBlob to JSON
- can't marshal format to JSON
- error creating config file contents
- error marshaling JSON
- error marshaling stats
AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07).
Data as JSON: /api/errors/257560bdb3c9fbb2.
Report an issue: GitHub.
Appendix: source
Thrown at repo/token.go:35
// Token returns an opaque token that contains repository connection information
// and optionally the provided password.
func (r *directRepository) Token(password string) (string, error) {
return EncodeToken(password, r.blobs.ConnectionInfo())
}
// EncodeToken returns an opaque token that contains the given connection information
// and optionally the provided password.
func EncodeToken(password string, ci blob.ConnectionInfo) (string, error) {
ti := &tokenInfo{
Version: "1",
Storage: ci,
Password: password,
}
v, err := json.Marshal(ti) //nolint:gosec // Password field needs to be included in token
if err != nil {
return "", errors.Wrap(err, "marshal token")
}
return base64.RawURLEncoding.EncodeToString(v), nil
}
// DecodeToken decodes the provided token and returns connection info and password if persisted.
func DecodeToken(token string) (blob.ConnectionInfo, string, error) {
t := &tokenInfo{}
v, err := base64.RawURLEncoding.DecodeString(token)
if err != nil {
return blob.ConnectionInfo{}, "", errors.New("unable to decode token")
}
if err := json.Unmarshal(v, t); err != nil {
return blob.ConnectionInfo{}, "", errors.New("unable to decode token")
}
View on GitHub (pinned to 82495e54b5)