dagger/dagger · error
resolve ID input %q: empty session ID
Error message
resolve ID input %q: empty session ID
What it means
Handle-form IDs are resolved relative to the session that produced the underlying result: the resolver looks the result up in the per-session cache using clientMetadata.SessionID. If the metadata exists but the session ID is empty, the lookup is impossible, so the resolver rejects the ID with this error.
Source
Thrown at dagql/call_request_input.go:113
}
return &ResultCallRef{Call: frame}, nil
}
frame, err := resultCallFromRecipeIDInput(ctx, id, memo)
if err != nil {
return nil, err
}
memo[dig] = frame
if frame == nil {
return nil, nil
}
return &ResultCallRef{Call: frame}, nil
}
clientMetadata, err := engine.ClientMetadataFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("resolve ID input %q: current client metadata: %w", idInputDebugString(id), err)
}
if clientMetadata.SessionID == "" {
return nil, fmt.Errorf("resolve ID input %q: empty session ID", idInputDebugString(id))
}
cache, err := EngineCache(ctx)
if err != nil {
return nil, fmt.Errorf("resolve ID input %q: current dagql cache: %w", idInputDebugString(id), err)
}
ref, err := cache.resultCallRefByResultID(ctx, clientMetadata.SessionID, id.EngineResultID())
if err != nil {
return nil, fmt.Errorf("resolve ID input %q: %w", idInputDebugString(id), err)
}
return ref, nil
}
func resultCallFromRecipeIDInput(ctx context.Context, id *call.ID, memo recipeCallMemo) (*ResultCall, error) {
if id == nil {
return nil, nil
}
if id.IsHandle() {
return nil, fmt.Errorf("handle-form IDs cannot be converted to inline recipe calls: %s", idInputDebugString(id))View on GitHub (pinned to 82ba2681db)
Solutions
- Connect through a properly established Dagger session so the request carries a non-empty session ID.
- If constructing client metadata manually, set SessionID to the request's actual session.
- Check middleware/interceptors for header stripping that removes the session ID before it reaches the engine.
Example fix
// before
md := &engine.ClientMetadata{ClientID: id}
// after
md := &engine.ClientMetadata{ClientID: id, SessionID: sessionID} Defensive patterns
Strategy: validation
Validate before calling
md, err := engine.ClientMetadataFromContext(ctx)
if err == nil && md.SessionID == "" {
return fmt.Errorf("session ID missing; connect via an established session")
} Try / catch
// catch and diagnose the empty-session case
if _, err := resultCallRefFromIDInput(ctx, id, memo); err != nil {
if strings.Contains(err.Error(), "empty session ID") {
// reconnect with a session-scoped client
}
} Prevention
- Use session-scoped clients rather than raw connections.
- Never hand-build ClientMetadata without filling SessionID.
- Audit middleware for header stripping that drops the session ID.
When it happens
Trigger: resultCallRefFromIDInput receives a handle-form ID while the ctx's client metadata has an empty SessionID — e.g. metadata constructed manually without a session ID, or a request path that clears/never assigns the session.
Common situations: Hand-built engine.ClientMetadata structs in tests or tools; proxy/interceptor layers that strip or replace request headers carrying the session ID; a client connecting without establishing a session (e.g. using a bare connection rather than a session-scoped one).
Related errors
- call %s.%s: empty session ID
- load %dth value from %T: empty session ID
- receiver: empty session ID
- lookup cache for digests: empty session ID
- wait: claim result: %w
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/c8e7ae5f0e28bcae.
Report an issue: GitHub.