pulumi/pulumi · error

expected a %s, got an asset

Error message

expected a %s, got an asset

What it means

unmarshalOutput writes a wire property value into a reflect destination (e.g. a typed result struct for a resource method's outputs). When the value is an Asset, the destination type must be assignable from the SDK's Asset interface (assetType); otherwise the SDK refuses with 'expected a %s, got an asset' (rpc.go:822), naming the destination type it actually received.

Source

Thrown at sdk/go/pulumi/rpc.go:822

	// pointer allocation to preserve the nil zero value for pointer destinations.
	if v.IsOutput() && v.OutputValue().Element.IsNull() {
		return v.OutputValue().Secret, nil
	}

	allocatedPointer := false
	// Allocate storage as necessary.
	for dest.Kind() == reflect.Pointer {
		allocatedPointer = true
		elem := reflect.New(dest.Type().Elem())
		dest.Set(elem)
		dest = elem.Elem()
	}

	// In the case of assets and archives, turn these into real asset and archive structures.
	switch {
	case v.IsAsset():
		if !assetType.AssignableTo(dest.Type()) {
			return false, fmt.Errorf("expected a %s, got an asset", dest.Type())
		}

		asset, secret, err := unmarshalPropertyValue(ctx, v)
		if err != nil {
			return false, err
		}
		dest.Set(reflect.ValueOf(asset))
		return secret, nil
	case v.IsArchive():
		if !archiveType.AssignableTo(dest.Type()) {
			return false, fmt.Errorf("expected a %s, got an archive", dest.Type())
		}

		archive, secret, err := unmarshalPropertyValue(ctx, v)
		if err != nil {
			return false, err
		}
		dest.Set(reflect.ValueOf(archive))

View on GitHub (pinned to 793f7b2e16)

Solutions

  1. Change the destination field's type to pulumi.Asset (or the concrete asset type) so it is assignable from assetType.
  2. Regenerate or update the provider's Go SDK so result structs match the schema's asset-typed properties.
  3. If the value should be a string, extract it at the call site from the concrete asset (e.g. type-switch to *FileAsset/*StringAsset/*RemoteAsset) rather than typing the field as string.
  4. Check the provider schema (`pulumi schema`) to confirm the property's expected type and align the struct.

Example fix

// before
type GetResult struct {
    Content string
}
// after
type GetResult struct {
    Content pulumi.Asset
}
Defensive patterns

Strategy: type-guard

Validate before calling

func expectsAsset(dest reflect.Value, v resource.PropertyValue) bool {
    return !v.IsAsset() || reflect.TypeOf((*pulumi.Asset)(nil)).Elem().AssignableTo(dest.Type())
}

Type guard

func destAcceptsAsset(destType reflect.Type) bool {
    return reflect.TypeOf((*pulumi.Asset)(nil)).Elem().AssignableTo(destType)
}

Try / catch

if err := result.Unmarshal(ctx, resp.Return); err != nil {
    if strings.Contains(err.Error(), "got an asset") {
        return fmt.Errorf("result struct field must be pulumi.Asset for asset-typed schema property: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Declaring an output/result field of a non-asset type (string, pulumi.StringOutput, interface{}, custom struct) while the engine returns an Asset for that property - e.g. in a resource method's Check/Invoke-style outputs struct or a GetX result field typed wrongly.

Common situations: Mismatch between the Pulumi schema (which says a property is an asset) and a hand-written Go result struct; copying a result struct from another language SDK where the property was a plain string; schema drift after a provider upgrade changed the property to an AssetOrArchive.

Related errors


AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31). Data as JSON: /api/errors/8c59fe88072ee9c0. Report an issue: GitHub.