hibiken/asynq · error

cannot encode nil message

Error message

cannot encode nil message

What it means

base.EncodeMessage rejects a nil *TaskMessage before attempting protobuf marshaling, returning 'cannot encode nil message'. Encoding a nil message would otherwise panic or produce a meaningless empty payload, so the library fails with an explicit error.

Solutions

  1. Check msg != nil before calling EncodeMessage and return a descriptive error early.
  2. Validate every element of a batch before enqueuing; skip or fail loudly on nil entries.
  3. Fix the constructor/builder that produced the nil message instead of guarding at the encode site.
  4. Use asynq.NewTask(...).Message() (higher-level API) so a nil message cannot be constructed accidentally.

Example fix

// before
payload, _ := base.EncodeMessage(msg)
// after
if msg == nil { return fmt.Errorf("task message not initialized") }
payload, err := base.EncodeMessage(msg)
Defensive patterns

Strategy: type-guard

Validate before calling

if msg == nil { return nil, fmt.Errorf("cannot encode nil message") }

Type guard

func msgReady(m *base.TaskMessage) bool { return m != nil && m.ID != "" && m.Type != "" && m.Queue != "" }

Prevention

When it happens

Trigger: Calling base.EncodeMessage(nil) directly, or indirectly via Enqueue/BatchEnqueue paths where a nil message was constructed (e.g. building a TaskMessage from a task whose fields were never populated and a nil deref-guard path hit).

Common situations: Custom enqueue code assembling TaskMessage structs where a nil pointer slips into a slice used for BatchEnqueue; reflection/generic helpers returning nil on error but the caller proceeding anyway.

Related errors


AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07). Data as JSON: /api/errors/a28cbedf94303738. Report an issue: GitHub.

Appendix: source

Thrown at internal/base/base.go:305

	// GroupKey holds the group key used for task aggregation.
	//
	// Empty string indicates no aggregation is used for this task.
	GroupKey string

	// Retention specifies the number of seconds the task should be retained after completion.
	Retention int64

	// CompletedAt is the time the task was processed successfully in Unix time,
	// the number of seconds elapsed since January 1, 1970 UTC.
	//
	// Use zero to indicate no value.
	CompletedAt int64
}

// EncodeMessage marshals the given task message and returns an encoded bytes.
func EncodeMessage(msg *TaskMessage) ([]byte, error) {
	if msg == nil {
		return nil, fmt.Errorf("cannot encode nil message")
	}
	return proto.Marshal(&pb.TaskMessage{
		Type:         msg.Type,
		Payload:      msg.Payload,
		Headers:      msg.Headers,
		Id:           msg.ID,
		Queue:        msg.Queue,
		Retry:        int32(msg.Retry),
		Retried:      int32(msg.Retried),
		ErrorMsg:     msg.ErrorMsg,
		LastFailedAt: msg.LastFailedAt,
		Timeout:      msg.Timeout,
		Deadline:     msg.Deadline,
		UniqueKey:    msg.UniqueKey,
		GroupKey:     msg.GroupKey,
		Retention:    msg.Retention,
		CompletedAt:  msg.CompletedAt,
	})

View on GitHub (pinned to d135f1439b)