Troubleshooting
This guide covers common issues and solutions when using Fory Go.
Error Types
Fory Go uses typed errors with specific error kinds:
type Error struct {
kind ErrorKind
message string
// Additional context fields
}
func (e Error) Kind() ErrorKind { return e.kind }
func (e Error) Error() string { return e.message }
Error Kinds
| Kind | Value | Description |
|---|---|---|
ErrKindOK | 0 | No error |
ErrKindBufferOutOfBound | 1 | Read/write beyond buffer bounds |
ErrKindTypeMismatch | 2 | Type ID mismatch |
ErrKindUnknownType | 3 | Unknown type encountered |
ErrKindSerializationFailed | 4 | General serialization failure |
ErrKindDeserializationFailed | 5 | General deserialization failure |
ErrKindMaxDepthExceeded | 6 | Recursion depth limit exceeded |
ErrKindNilPointer | 7 | Unexpected nil pointer |
ErrKindInvalidRefId | 8 | Invalid reference ID |
ErrKindHashMismatch | 9 | Struct hash mismatch |
ErrKindInvalidTag | 10 | Invalid fory struct tag |
Common Errors and Solutions
ErrKindUnknownType
Error: unknown type encountered
Cause: Type not registered before serialization/deserialization.
Solution:
f := fory.New()
// Register type before use
f.RegisterStruct(User{}, 1)
// Now serialization works
data, _ := f.Serialize(&User{ID: 1})
ErrKindTypeMismatch
Error: type mismatch: expected X, got Y
Cause: Serialized data has different type than expected.
Solutions:
- Use correct target type:
// Wrong: Deserializing User into Order
var order Order
f.Deserialize(userData, &order) // Error!
// Correct
var user User
f.Deserialize(userData, &user)
- Ensure consistent registration:
// Serializer
f1 := fory.New()
f1.RegisterStruct(User{}, 1)
// Deserializer - must use same ID
f2 := fory.New()
f2.RegisterStruct(User{}, 1) // Same ID!
ErrKindHashMismatch
Error: hash X is not consistent with Y for type Z
Cause: Struct definition changed between serialization and deserialization.
Solutions:
- Enable compatible mode:
f := fory.New(fory.WithCompatible(true))
- Ensure struct definitions match:
// Both serializer and deserializer must have same struct
type User struct {
ID int64
Name string
}
- Regenerate codegen (if using):
go generate ./...
ErrKindMaxDepthExceeded
Error: max depth exceeded
Cause: Data nesting exceeds maximum depth limit.
Possible causes:
- Deeply nested data structures exceeding the default limit (20)
- Unintended circular references without reference tracking enabled
- Malicious data: Attackers may craft deeply nested payloads to cause resource exhaustion
Solutions:
- Increase max depth (default is 20):
f := fory.New(fory.WithMaxDepth(50))
- Enable reference tracking (for circular data):
f := fory.New(fory.WithTrackRef(true))
-
Check for unintended circular references in your data.
-
Validate untrusted data: When deserializing data from untrusted sources, do not blindly increase max depth. Consider validating input size and structure before deserialization.
ErrKindBufferOutOfBound
Error: buffer out of bound: offset=X, need=Y, size=Z
Cause: Reading beyond available data.
Solutions:
- Ensure complete data transfer:
// Wrong: Truncated data
data := fullData[:100]
f.Deserialize(data, &target) // Error if data was larger
// Correct: Use full data
f.Deserialize(fullData, &target)
- Check for data corruption: Verify data integrity during transmission.
ErrKindInvalidRefId
Error: invalid reference ID
Cause: Reference to non-existent object in serialized data.
Solutions:
- Ensure reference tracking consistency:
// Serializer and deserializer must have same setting
f1 := fory.New(fory.WithTrackRef(true))
f2 := fory.New(fory.WithTrackRef(true)) // Must match!
- Check for data corruption.
ErrKindInvalidTag
Error: invalid fory struct tag
Cause: Invalid struct tag configuration.
Common causes:
- Invalid tag ID: ID must be >= -1
// Wrong: negative ID (other than -1)
type Bad struct {
Field int `fory:"id=-5"`
}
// Correct
type Good struct {
Field int `fory:"id=0"`
}
- Duplicate tag IDs: Each field must have a unique ID within the struct
// Wrong: duplicate IDs
type Bad struct {
Field1 int `fory:"id=0"`
Field2 int `fory:"id=0"` // Duplicate!
}
// Correct
type Good struct {
Field1 int `fory:"id=0"`
Field2 int `fory:"id=1"`
}
Cross-Language Issues
Field Order Mismatch
Symptom: Data deserializes but fields have wrong values.
Cause: Different field ordering between languages. In non-compatible mode, fields are sorted by their snake_case names. CamelCase field names (e.g., FirstName) are converted to snake_case (e.g., first_name) for sorting.
Solutions:
- Ensure converted snake_case names are consistent: Field names across languages must produce the same snake_case ordering:
type User struct {
FirstName string // Go: FirstName -> first_name
LastName string // Go: LastName -> last_name
// Sorted alphabetically by snake_case: first_name, last_name
}
- Use field IDs for consistent ordering: Field IDs (non-negative integers) act as aliases for field names, used for both sorting and field matching during deserialization:
type User struct {
FirstName string `fory:"id=0"`
LastName string `fory:"id=1"`
}
Ensure the same field IDs are used across all languages for corresponding fields.
Name Registration Mismatch
Symptom: unknown type in other languages.
Solution: Use identical names:
// Go
f.RegisterNamedStruct(User{}, "example.User")
// Java - must match exactly
fory.register(User.class, "example.User");
// Python
fory.register(User, typename="example.User")
Performance Issues
Slow Serialization
Possible causes:
-
Large object graphs: Reduce data size or serialize incrementally.
-
Excessive reference tracking: Disable if not needed:
f := fory.New(fory.WithTrackRef(false))
- Deep nesting: Flatten data structures where possible.
High Memory Usage
Possible causes:
-
Large serialized data: Process in chunks.
-
Reference tracking overhead: Disable if not needed.
-
Buffer not released: Reuse buffers:
buf := fory.NewByteBuffer(nil)
f.SerializeTo(buf, value)
// Process data
buf.Reset() // Reuse for next serialization
Thread Contention
Symptom: Slowdown under concurrent load.
Solutions:
- Use per-goroutine instances for hot paths:
func worker() {
f := fory.New() // Each worker has own instance
for task := range tasks {
f.Serialize(task)
}
}
- Profile pool usage with thread-safe wrapper.
Debugging Techniques
Enable Debug Output
Set environment variable:
ENABLE_FORY_DEBUG_OUTPUT=1 go test ./...
Inspect Serialized Data
data, _ := f.Serialize(value)
fmt.Printf("Serialized %d bytes\n", len(data))
fmt.Printf("Header: %x\n", data[:4]) // Magic + flags
Check Type Registration
// Verify type is registered
f := fory.New()
err := f.RegisterStruct(User{}, 1)
if err != nil {
fmt.Printf("Registration failed: %v\n", err)
}
Compare Struct Hashes
If getting hash mismatch, compare struct definitions:
// Print struct info for debugging
t := reflect.TypeOf(User{})
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Printf("Field: %s, Type: %s\n", f.Name, f.Type)
}
Testing Tips
Test Round-Trip
func TestRoundTrip(t *testing.T) {
f := fory.New()
f.RegisterStruct(User{}, 1)
original := &User{ID: 1, Name: "Alice"}
data, err := f.Serialize(original)
require.NoError(t, err)
var result User
err = f.Deserialize(data, &result)
require.NoError(t, err)
assert.Equal(t, original.ID, result.ID)
assert.Equal(t, original.Name, result.Name)
}
Test Cross-Language
cd java/fory-core
FORY_GO_JAVA_CI=1 mvn test -Dtest=org.apache.fory.xlang.GoXlangTest
Test Schema Evolution
func TestSchemaEvolution(t *testing.T) {
f1 := fory.New(fory.WithCompatible(true))
f1.RegisterStruct(UserV1{}, 1)
data, _ := f1.Serialize(&UserV1{ID: 1, Name: "Alice"})
f2 := fory.New(fory.WithCompatible(true))
f2.RegisterStruct(UserV2{}, 1)
var result UserV2
err := f2.Deserialize(data, &result)
require.NoError(t, err)
}
Getting Help
If you encounter issues not covered here:
- Check GitHub Issues: github.com/apache/fory/issues
- Enable debug output:
ENABLE_FORY_DEBUG_OUTPUT=1 - Create minimal reproduction: Isolate the problem
- Report the issue: Include Go version, Fory version, and minimal code