跳到主要内容
版本:dev

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

KindValueDescription
ErrKindOK0No error
ErrKindBufferOutOfBound1Read/write beyond buffer bounds
ErrKindTypeMismatch2Type ID mismatch
ErrKindUnknownType3Unknown type encountered
ErrKindSerializationFailed4General serialization failure
ErrKindDeserializationFailed5General deserialization failure
ErrKindMaxDepthExceeded6Recursion depth limit exceeded
ErrKindNilPointer7Unexpected nil pointer
ErrKindInvalidRefId8Invalid reference ID
ErrKindHashMismatch9Struct hash mismatch
ErrKindInvalidTag10Invalid 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:

  1. 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)
  1. 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:

  1. Enable compatible mode:
f := fory.New(fory.WithCompatible(true))
  1. Ensure struct definitions match:
// Both serializer and deserializer must have same struct
type User struct {
ID int64
Name string
}
  1. 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:

  1. Increase max depth (default is 20):
f := fory.New(fory.WithMaxDepth(50))
  1. Enable reference tracking (for circular data):
f := fory.New(fory.WithTrackRef(true))
  1. Check for unintended circular references in your data.

  2. 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:

  1. 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)
  1. Check for data corruption: Verify data integrity during transmission.

ErrKindInvalidRefId

Error: invalid reference ID

Cause: Reference to non-existent object in serialized data.

Solutions:

  1. 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!
  1. Check for data corruption.

ErrKindInvalidTag

Error: invalid fory struct tag

Cause: Invalid struct tag configuration.

Common causes:

  1. 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"`
}
  1. 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:

  1. 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
}
  1. 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:

  1. Large object graphs: Reduce data size or serialize incrementally.

  2. Excessive reference tracking: Disable if not needed:

f := fory.New(fory.WithTrackRef(false))
  1. Deep nesting: Flatten data structures where possible.

High Memory Usage

Possible causes:

  1. Large serialized data: Process in chunks.

  2. Reference tracking overhead: Disable if not needed.

  3. 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:

  1. Use per-goroutine instances for hot paths:
func worker() {
f := fory.New() // Each worker has own instance
for task := range tasks {
f.Serialize(task)
}
}
  1. 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:

  1. Check GitHub Issues: github.com/apache/fory/issues
  2. Enable debug output: ENABLE_FORY_DEBUG_OUTPUT=1
  3. Create minimal reproduction: Isolate the problem
  4. Report the issue: Include Go version, Fory version, and minimal code