{"record":{"id":"257560bdb3c9fbb2","repo":"kopia/kopia","slug":"marshal-token","errorCode":null,"errorMessage":"marshal token","messagePattern":"marshal token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"repo/token.go","lineNumber":35,"sourceCode":"\n// Token returns an opaque token that contains repository connection information\n// and optionally the provided password.\nfunc (r *directRepository) Token(password string) (string, error) {\n\treturn EncodeToken(password, r.blobs.ConnectionInfo())\n}\n\n// EncodeToken returns an opaque token that contains the given connection information\n// and optionally the provided password.\nfunc EncodeToken(password string, ci blob.ConnectionInfo) (string, error) {\n\tti := &tokenInfo{\n\t\tVersion:  \"1\",\n\t\tStorage:  ci,\n\t\tPassword: password,\n\t}\n\n\tv, err := json.Marshal(ti) //nolint:gosec // Password field needs to be included in token\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"marshal token\")\n\t}\n\n\treturn base64.RawURLEncoding.EncodeToString(v), nil\n}\n\n// DecodeToken decodes the provided token and returns connection info and password if persisted.\nfunc DecodeToken(token string) (blob.ConnectionInfo, string, error) {\n\tt := &tokenInfo{}\n\n\tv, err := base64.RawURLEncoding.DecodeString(token)\n\tif err != nil {\n\t\treturn blob.ConnectionInfo{}, \"\", errors.New(\"unable to decode token\")\n\t}\n\n\tif err := json.Unmarshal(v, t); err != nil {\n\t\treturn blob.ConnectionInfo{}, \"\", errors.New(\"unable to decode token\")\n\t}\n","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/kopia/kopia/blob/82495e54b584c1ef6073c9e1be048f57f8aef078/repo/token.go#L17-L53","documentation":"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.","triggerScenarios":"Calling repo.EncodeToken(ci, password) when the underlying TokenInfo struct (containing storage.ConnectionInfo and password) cannot be JSON-marshaled by encoding/json.","commonSituations":"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.","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."],"exampleFix":"// before\nci.Config = someFuncField // func type - not JSON marshalable\nv, err := json.Marshal(ti)\n// after\nci.Config = serializedConfigString // JSON-safe representation\nv, err := json.Marshal(ti)","handlingStrategy":"try-catch","validationCode":"// ensure token info fields are JSON-serializable before encoding\nif err := json.Valid([]byte(\"{}\")); err != nil { /* env issue */ }\n// or attempt a dry-run marshal of the ConnectionInfo config\nif _, err := json.Marshal(ci.Config); err != nil {\n    return fmt.Errorf(\"connection info not JSON-serializable: %w\", err)\n}","typeGuard":"func isJSONSerializable(v any) bool {\n    _, err := json.Marshal(v)\n    return err == nil\n}","tryCatchPattern":"token, err := repo.EncodeToken(ci, password)\nif err != nil {\n    var jsonErr *json.UnsupportedTypeError\n    if errors.As(err, &jsonErr) {\n        // handle non-serializable field: jsonErr.Type\n    }\n    return fmt.Errorf(\"token encoding failed: %w\", err)\n}","preventionTips":["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."],"tags":["json","serialization","token"],"backgroundTag":"json-marshal-failed","analyzedSha":"82495e54b584c1ef6073c9e1be048f57f8aef078","analyzedAt":"2026-09-07T20:35:21.689Z","contentChangedAt":"2026-09-07T20:35:21.689Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}