dagger/dagger · error

encode persisted search submatch: nil search submatch

Error message

encode persisted search submatch: nil search submatch

What it means

SearchSubmatch implements the dagql PersistedObject interface. This guard fires when EncodePersistedObject is called on a nil *SearchSubmatch receiver; a nil submatch cannot be serialized, so an explicit error is returned. Note the SearchResult encoder itself skips nil submatches, so this indicates a direct encoding of a nil submatch elsewhere.

Source

Thrown at core/search.go:121

var _ dagql.PersistedObject = (*SearchSubmatch)(nil)
var _ dagql.PersistedObjectDecoder = (*SearchSubmatch)(nil)

func (*SearchSubmatch) Type() *ast.Type {
	return &ast.Type{
		NamedType: "SearchSubmatch",
		NonNull:   true,
	}
}

type persistedSearchSubmatch struct {
	Text  string `json:"text"`
	Start int    `json:"start"`
	End   int    `json:"end"`
}

func (m *SearchSubmatch) EncodePersistedObject(context.Context, dagql.PersistedObjectCache) (dagql.PersistedObjectEncoding, error) {
	if m == nil {
		return dagql.PersistedObjectEncoding{}, fmt.Errorf("encode persisted search submatch: nil search submatch")
	}
	return encodePersistedObjectPayload(persistedSearchSubmatch{
		Text:  m.Text,
		Start: m.Start,
		End:   m.End,
	})
}

func (*SearchSubmatch) DecodePersistedObject(_ context.Context, _ *dagql.Server, _ uint64, _ *dagql.ResultCall, payload json.RawMessage) (dagql.Typed, error) {
	var persisted persistedSearchSubmatch
	if err := json.Unmarshal(payload, &persisted); err != nil {
		return nil, fmt.Errorf("decode persisted search submatch payload: %w", err)
	}
	return &SearchSubmatch{
		Text:  persisted.Text,
		Start: persisted.Start,
		End:   persisted.End,
	}, nil

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Filter nil submatches before persisting search results
  2. Upgrade Dagger to a version where this path skips nils
  3. Re-run the query to regenerate the cache entry
  4. File an issue with the query if it reproduces

Example fix

// before
subs := r.Submatches
// after
subs := make([]*SearchSubmatch, 0, len(r.Submatches))
for _, m := range r.Submatches {
  if m != nil {
    subs = append(subs, m)
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// drop nil submatches before persisting
subs := make([]*SearchSubmatch, 0, len(r.Submatches))
for _, m := range r.Submatches {
  if m != nil { subs = append(subs, m) }
}

Type guard

func isValidSubmatch(m *SearchSubmatch) bool { return m != nil }

Prevention

When it happens

Trigger: An internal cache/encoding path calls EncodePersistedObject directly on a nil *SearchSubmatch, e.g. a submatch slice entry that was nil being persisted individually by the dagql cache.

Common situations: Engine-internal bug where a nil submatch reaches the persisted-object cache; typically surfaced as an unexpected failure while caching search results in a saved operation/replay.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/322a782b4fdf38e0. Report an issue: GitHub.