Skip to content

Transforms fail with DnsRecord resource and custom provider #1398

@elenafayethomas

Description

@elenafayethomas

Describe what happened

A custom Pulumi provider has a Tagger component. This component has a transform that modifies the tags of a Cloudflare DnsRecord resource.

This works with v5 of the Cloudflare provider but not with v6.0.0 and upwards.

On running pulumi preview the program errors with:

pulumi:pulumi:Stack (pulumi-typescript-dev):
    The Pulumi runtime detected that 72 promises were still active
    at the time that the process exited. There are a few ways that this can occur:
      * Not using `await` or `.then` on a Promise returned from a Pulumi API
      * Introducing a cyclic dependency between two Pulumi Resources
      * A bug in the Pulumi Runtime
    Leaving promises active is probably not what you want. If you are unsure about
    why you are seeing this message, re-run your program with the `PULUMI_DEBUG_PROMISE_LEAK
S`
    environment variable. The Pulumi runtime will then print out additional
    debug information about the leaked promises.

    error: an unhandled error occurred: Program exited with non-zero exit code: 1

The provider code is as follows:

func NewTagger(ctx *pulumi.Context, name string, args *TaggerArgs, opts ...pulumi.ResourceOption) (*Tagger, error) {
	component := &Tagger{}
	err := ctx.RegisterComponentResource(p.GetTypeToken(ctx.Context()), name, component, opts...)

	if err != nil {
		return nil, err
	}

	err = component.build(ctx, args) // sets all the tags with sensible defaults

	if err != nil {
		return nil, err
	}

	ctx.RegisterResourceTransform(
		func(_ context.Context, tArgs *pulumi.ResourceTransformArgs) *pulumi.ResourceTransformResult {
			// this can be generalised by provider. For example:
			// `if strings.HasPrefix(args.Type, "<provider>:") { ... }`
			if strings.HasPrefix(tArgs.Type, "aws:") {
				tArgs.Props["tags"] = component.aws(tArgs.Props["tags"])

				return &pulumi.ResourceTransformResult{
					Props: tArgs.Props,
					Opts:  tArgs.Opts,
				}
			} else if tArgs.Type == "cloudflare:index/dnsRecord:DnsRecord" {
				tArgs.Props["tags"] = component.cloudflare(tArgs.Props["tags"])
				return &pulumi.ResourceTransformResult{ // ERRORS HERE
					Props: tArgs.Props,
					Opts: tArgs.Opts,
				}
			}
			return nil
		},
	)

	return component, nil
}

Sample program

import * as pulumi from "@pulumi/pulumi"
import * as cloudflare from "@pulumi/cloudflare"
import * as custom from "@platform/example"

const tagger = new custom.config.Tagger('tagger', {
	service: 'pulumi-test',
})

const opts: pulumi.ResourceOptions = {
	parent: tagger,
}

async function create(opts: pulumi.ResourceOptions) {
	const zone = cloudflare.getZone({ zoneId: process.env.CLOUDFLARE_ZONE_ID })

	new cloudflare.Record('example', {
		zoneId: await zone.then((zone) => zone.zoneId!),
		name: '<subdomain_name>',
		content: '<load_balancer_url>',
		type: 'CNAME',
		ttl: 300,
		tags: ["CustomTag:some-value"],
	}, opts)
}

create(opts)

Log output

$ pulumi preview
Previewing update (dev):
     Type                         Name                   Plan       Info
 +   pulumi:pulumi:Stack          pulumi-typescript-dev  create     1 error; 9 messages
     ├─ pulumi:providers:example  default_0_1_0                     1 warning
 +   └─ example:config:Tagger     tagger                 create     

Diagnostics:
  pulumi:pulumi:Stack (pulumi-typescript-dev):
    error: an unhandled error occurred: Program exited with non-zero exit code: 1

    The Pulumi runtime detected that 72 promises were still active
    at the time that the process exited. There are a few ways that this can occur:
      * Not using `await` or `.then` on a Promise returned from a Pulumi API
      * Introducing a cyclic dependency between two Pulumi Resources
      * A bug in the Pulumi Runtime
    Leaving promises active is probably not what you want. If you are unsure about
    why you are seeing this message, re-run your program with the `PULUMI_DEBUG_PROMISE_LEAK
S`
    environment variable. The Pulumi runtime will then print out additional
    debug information about the leaked promises.

  pulumi:providers:example (default_0_1_0):
    warning: provider attempted to use __internal key that is reserved by the engine

Resources:
    + 2 to create
    1 errored

With PULUMI_DEBUT_PROMISE_LEAKS=true the logs are far more detailed. They are too long to share here but I can send them to help with debugging.

Affected Resource(s)

DnsRecord

Output of pulumi about

Version      3.203.0
Go Version   go1.25.3
Go Compiler  gc

Plugins
KIND      NAME                       VERSION
resource  aws                        6.83.0
resource  cloudflare                 6.10.0
resource  example                    0.1.0
language  nodejs                     3.203.0
resource  random                     4.18.2

Host     
OS       ubuntu
Version  22.04
Arch     x86_64

This project is written in nodejs: executable='/home/elena/.nvm/versions/node/v24.5.0/bin/no
de' version='v24.5.0'

Current Stack: organization/pulumi-typescript/dev

Found no resources associated with dev

Found no pending operations associated with dev

Backend        
Name           <redacted>
URL            <redacted_s3_backend>
User           elena
Organizations  
Token type     personal

Dependencies:
NAME                                  VERSION
@pulumi/pulumi                        3.181.0
@types/node                           18.19.115
typescript                            5.8.3
@platform/example                     0.1.0
@pulumi/aws                           6.83.0
@pulumi/cloudflare                    6.10.0
@pulumi/random                        4.18.2
ts-node                               10.9.2

Pulumi locates its logs in /tmp by default

Additional context

The custom provider is attempting to tag resources from various providers, including AWS. The aim is to have a centralised tagging resource that handles tagging for all other resources in a stack.

Contributing

Vote on this issue by adding a 👍 reaction.
To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugSome behavior is incorrect or out of specneeds-triageNeeds attention from the triage team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions