{"record":{"id":"96e99797a07d9e2c","repo":"hibiken/asynq","slug":"cannot-encode-nil-enqueue-event","errorCode":null,"errorMessage":"cannot encode nil enqueue event","messagePattern":"cannot encode nil enqueue event","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/base/base.go","lineNumber":560,"sourceCode":"\t\tNext:    next.AsTime(),\n\t\tPrev:    prev.AsTime(),\n\t}, nil\n}\n\n// SchedulerEnqueueEvent holds information about an enqueue event by a scheduler.\ntype SchedulerEnqueueEvent struct {\n\t// ID of the task that was enqueued.\n\tTaskID string\n\n\t// Time the task was enqueued.\n\tEnqueuedAt time.Time\n}\n\n// EncodeSchedulerEnqueueEvent marshals the given event\n// and returns an encoded bytes.\nfunc EncodeSchedulerEnqueueEvent(event *SchedulerEnqueueEvent) ([]byte, error) {\n\tif event == nil {\n\t\treturn nil, fmt.Errorf(\"cannot encode nil enqueue event\")\n\t}\n\tenqueuedAt := timestamppb.New(event.EnqueuedAt)\n\treturn proto.Marshal(&pb.SchedulerEnqueueEvent{\n\t\tTaskId:      event.TaskID,\n\t\tEnqueueTime: enqueuedAt,\n\t})\n}\n\n// DecodeSchedulerEnqueueEvent unmarshals the given bytes\n// and returns a decoded SchedulerEnqueueEvent.\nfunc DecodeSchedulerEnqueueEvent(b []byte) (*SchedulerEnqueueEvent, error) {\n\tvar pbmsg pb.SchedulerEnqueueEvent\n\tif err := proto.Unmarshal(b, &pbmsg); err != nil {\n\t\treturn nil, err\n\t}\n\tenqueuedAt := pbmsg.GetEnqueueTime()\n\treturn &SchedulerEnqueueEvent{\n\t\tTaskID:     pbmsg.GetTaskId(),","sourceCodeStart":542,"sourceCodeEnd":578,"githubUrl":"https://github.com/hibiken/asynq/blob/d135f1439bee74e989b7f9b41ecd542cc87f024a/internal/base/base.go#L542-L578","documentation":"EncodeSchedulerEnqueueEvent marshals a *SchedulerEnqueueEvent (a record that a periodic task enqueued a task, with EnqueuedAt timestamp) into protobuf bytes for persistence in Redis. The library throws this when the event pointer is nil, since there is nothing to encode. It protects proto.Marshal from a nil dereference.","triggerScenarios":"Calling base.EncodeSchedulerEnqueueEvent(nil) directly, or passing the result of an event-producing function that returned nil on a failure branch without checking it first.","commonSituations":"Custom event loggers that record periodic-task enqueue events and pass through values from a function returning (*Event, error) but ignoring the error; test fixtures with uninitialized event pointers.","solutions":["Check err and the pointer before encoding: only call EncodeSchedulerEnqueueEvent with a non-nil event.","Fix the event producer to always return a populated event or a non-nil error.","Skip/log nil events in bulk-write loops instead of attempting to encode them."],"exampleFix":"// before\nev, _ := newEnqueueEvent(task) // error ignored, ev may be nil\nb, err := base.EncodeSchedulerEnqueueEvent(ev)\n// after\nev, err := newEnqueueEvent(task)\nif err != nil {\n    return err\n}\nif ev == nil {\n    return nil // or log and skip\n}\nb, err := base.EncodeSchedulerEnqueueEvent(ev)","handlingStrategy":"validation","validationCode":"if event == nil {\n    return errors.New(\"refusing to encode nil enqueue event\")\n}","typeGuard":"func validEvent(e *base.SchedulerEnqueueEvent) bool { return e != nil && e.TaskID != \"\" }","tryCatchPattern":"b, err := base.EncodeSchedulerEnqueueEvent(ev)\nif err != nil {\n    return fmt.Errorf(\"encode enqueue event: %w\", err)\n}","preventionTips":["Always check the error alongside the pointer from event-producing functions.","Skip or log nil events in bulk event-writing loops.","Construct events via a single constructor that validates required fields."],"tags":["go","nil-pointer","serialization","scheduler"],"backgroundTag":"null-argument","analyzedSha":"d135f1439bee74e989b7f9b41ecd542cc87f024a","analyzedAt":"2026-09-07T19:02:34.660Z","contentChangedAt":"2026-09-07T19:02:34.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}