getsops/sops · error

Error marshaling timestamp

Error message

Error marshaling timestamp %q: %w

What it means

When the plaintext value is a time.Time, Encrypt serializes it with MarshalText before sealing. If the timestamp cannot be marshaled (only possible for out-of-range or corrupt time.Time values such as year > 9999 in some formats), this error wraps both the value and the underlying failure. It is one of the typed branches of the plaintext switch.

Solutions

  1. Validate the time.Time (e.g. value.IsZero(), finite year) before passing it to Encrypt.
  2. Reconstruct the timestamp by re-parsing the source value with time.Parse(time.RFC3339, ...).
  3. If the value came from a corrupted file, restore the file or re-enter the timestamp.

Example fix

// before
value := time.Time{}
cipher.Encrypt(value, key, ad)
// after
if value.IsZero() {
    value = time.Now().UTC()
}
cipher.Encrypt(value, key, ad)
Defensive patterns

Strategy: validation

Validate before calling

func validTime(t time.Time) bool {
    return !t.IsZero() && t.Year() >= 0 && t.Year() <= 9999
}

Type guard

if ts, ok := plaintext.(time.Time); ok && ts.IsZero() {
    return fmt.Errorf("refusing to encrypt zero timestamp")
}

Try / catch

ciphertext, err := cipher.Encrypt(v, key, ad)
if err != nil && strings.Contains(err.Error(), "Error marshaling timestamp") {
    // rebuild the timestamp from the raw source value
}

Prevention

When it happens

Trigger: Calling Cipher.Encrypt with a time.Time value whose MarshalText fails — e.g. a zero-initialized or malformed time with out-of-range fields produced by a corrupted tree or bad deserialization.

Common situations: Programmatically constructing sops trees with hand-built time.Time values containing NaN/extreme components; corrupted metadata parsed from a damaged file.

Related errors


AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01). Data as JSON: /api/errors/b1cf2ad363d806bb. Report an issue: GitHub.

Appendix: source

Thrown at aes/cipher.go:188

		encryptedType = "int"
		plainBytes = []byte(strconv.Itoa(value))
	case float64:
		encryptedType = "float"
		// The Python version encodes floats without padding 0s after the decimal point.
		plainBytes = []byte(strconv.FormatFloat(value, 'f', -1, 64))
	case bool:
		encryptedType = "bool"
		// The Python version encodes booleans with Titlecase
		if value {
			plainBytes = []byte("True")
		} else {
			plainBytes = []byte("False")
		}
	case time.Time:
		encryptedType = "time"
		plainBytes, err = value.MarshalText()
		if err != nil {
			return "", fmt.Errorf("Error marshaling timestamp %q: %w", value, err)
		}
	case sops.Comment:
		encryptedType = "comment"
		plainBytes = []byte(value.Value)
	default:
		return "", fmt.Errorf("Value to encrypt has unsupported type %T", value)
	}
	out := gcm.Seal(nil, iv, plainBytes, []byte(additionalData))
	return fmt.Sprintf("ENC[AES256_GCM,data:%s,iv:%s,tag:%s,type:%s]",
		base64.StdEncoding.EncodeToString(out[:len(out)-cryptoaes.BlockSize]),
		base64.StdEncoding.EncodeToString(iv),
		base64.StdEncoding.EncodeToString(out[len(out)-cryptoaes.BlockSize:]),
		encryptedType), nil
}

View on GitHub (pinned to 13442bb981)