dagger/dagger · error

structural input ref cannot have both result and digest

Error message

structural input ref cannot have both result and digest

What it means

Returned by ResultCallStructuralInputRef.Validate when the ref has both a Result pointer and a Digest set. A structural input identifies an input either by live result reference or by digest; specifying both is contradictory and rejected by this validation guard.

Source

Thrown at dagql/result_call_frame.go:1012

			}
			return "", nil, fmt.Errorf("result call frame %q structural inputs: %w", field, err)
		}
	}

	return digest.Digest(h.DigestAndClose()), inputRefs, nil
}

func (frame *ResultCall) AllEffectIDs() ([]string, error) {
	// FIXME: effect IDs are currently broken and only feed telemetry. Re-implement
	// them properly before restoring recursive effect traversal over result-call
	// refs and inputs.
	return []string{}, nil
}

func (ref ResultCallStructuralInputRef) Validate() error {
	switch {
	case ref.Result != nil && ref.Digest != "":
		return fmt.Errorf("structural input ref cannot have both result and digest")
	case ref.Result != nil:
		return ref.Result.Validate()
	case ref.Result == nil && ref.Digest == "":
		return fmt.Errorf("structural input ref must have either result or digest")
	default:
		return nil
	}
}

func (ref ResultCallStructuralInputRef) InputDigest(ctx context.Context) (digest.Digest, error) {
	c, err := EngineCache(ctx)
	if err != nil {
		return "", err
	}
	return ref.inputDigest(c)
}

func (ref ResultCallStructuralInputRef) inputDigest(c *Cache) (digest.Digest, error) {

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Set only Result (the digest is computed on demand) or only Digest, never both
  2. Clear the Digest field when a Result is available, or vice versa
  3. Prefer constructing via a helper constructor that enforces mutual exclusion

Example fix

// before
ref := ResultCallStructuralInputRef{Result: r, Digest: d}
// after
ref := ResultCallStructuralInputRef{Result: r}
Defensive patterns

Strategy: validation

Validate before calling

if ref.Result != nil && ref.Digest != "" {
    return fmt.Errorf("choose either Result or Digest, not both")
}

Type guard

func hasExactlyOneSource(ref ResultCallStructuralInputRef) bool {
    return (ref.Result != nil) != (ref.Digest != "")
}

Prevention

When it happens

Trigger: Constructing ResultCallStructuralInputRef{Result: r, Digest: d} with both fields non-zero, then calling Validate().

Common situations: Merging two code paths (one that used result refs, one that used digests) and combining fields; copy/paste construction that sets both for 'safety'.

Related errors


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