Upgrade Provider Errors
Use this file when the upgrade-provider tool fails and you need concrete fixes.
For patch edits/removals/rebases, follow the upstream-patches skill workflow. Preserve --no-submit on every upgrade-provider retry.
Patched upstream has interrupted or unexpected Git state
The tool deliberately leaves active Git operations, pulumi/patch-checkout, and unexpected branches unchanged because it cannot prove that an interrupted checkout applied the complete patch stack.
Preserve work by default:
- Inspect
git -C upstream statusand identify whethergit am, rebase, merge, cherry-pick, or another operation is active. - Use skill
upstream-patchesfor conflict resolution or patch edits. - Complete or safely abort the active operation. If checkout was interrupted during
git am, verify that everypatches/*.patchwas applied; later patch files may not have been reached. - Ensure the patch stack was rebased onto the requested target. If needed, run the target rebase from the provider root and resolve it fully:
./scripts/upstream.sh rebase -o refs/tags/v<TARGET>- Once every patch is applied and the target rebase is complete, write patches back and exit checkout mode:
./scripts/upstream.sh check_in- Confirm
upstreamis no longer onpulumi/patch-checkout, then rerunupgrade-providerwith--no-submit.
Only when intentionally discarding all interrupted patch work may you run:
./scripts/upstream.sh init -fThis deletes active operation state and patch-checkout work and can clean untracked files. Treat it as destructive discard, not normal recovery.
Patch conflicts during rebase
The tool uses scripts/upstream.sh to apply patch commits in the upstream submodule. If a patch no longer applies cleanly, you will see rebase errors like:
error: could not apply 83b04967e... docs patching
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".Fix from the upstream directory, following upstream-patches defaults (edit the owning patch commit; do not create a new patch unless asked):
- Identify conflicted files.
- Resolve conflicts while preserving the intent of the patch in
patches/. - Search for conflict markers and remove all of them before continuing.
git addthe resolved files.git rebase --continue.- Repeat until the rebase is complete, then return to the provider root and exit checkout mode:
./scripts/upstream.sh check_in- Confirm
upstreamis detached rather than onpulumi/patch-checkout, then rerunupgrade-providerwith--no-submit.
If git rebase --continue opens an editor in automation contexts, run GIT_EDITOR=true git rebase --continue.
Avoid:
- Rerunning the tool before
check_in; safe patched-provider preflight will reject leftover checkout state. - Hand-editing
patches/*.patchunless intentionally doing raw patch surgery. - Direct edits under
upstream/outsidecheckout/check_inworkflow.
Patch intent guidance
- Docs-related patches usually replace or remove Terraform references. Preserve those changes when resolving conflicts.
- A small, single-purpose docs patch that only removes an untranslatable section — for example an
## Importsection written in Terraform-only import syntax — can sometimes be retired entirely and replaced with a targeted docs edit rule inprovider/resources.gorather than resolving the conflict. Add the rule toProviderInfo.DocRules.EditRules(e.g. aSkipSectionByHeader-style rule that drops the section by its header); verify the exact helper against thepulumi-terraform-bridgeversion inprovider/go.mod. This avoids re-resolving the same conflict on every future upgrade, and when the patch was the repo's only one it can also remove the need for theupstreamsubmodule (delete the.gitmodulesentry). - Do not treat this as a default conflict-resolution tactic. Wholesale-replacing a docs patch with an edit rule only works when the patch is small and its intent maps cleanly onto a small rule. Patches that span multiple files or perform substantive rewrites should have their conflict resolved in place (per the guidance above), not be converted. Keep docs edit rules small and targeted — if reproducing the patch would require a large or brittle rule, resolve the conflict instead of converting it.
Upstream migrated from SDKv2 to Plugin Framework
Treat an upstream Terraform Plugin SDKv2-to-Plugin-Framework migration as a known migration path, not by itself as an architectural blocker. Common signals include:
- Compiler errors such as
undefined: <package>.Provideror code that still expects*schema.Provider. - Upstream
go.moddropping or makingterraform-plugin-sdk/v2indirect while addingterraform-plugin-framework. - Upstream release notes or commits that announce a Plugin Framework migration or SDKv2 removal.
make tfgenorupgrade-providererrors that a token mapping inprovider/resources.goreferences a resource that is no longer present in the provider, and suggests removing that mapping. An unmuxed SDKv2 bridge (shimv2.NewProvider) can only see resources registered in the upstream SDKv2ResourcesMap; a resource that upstream moved to the Plugin Framework is absent from the shimmed map, so its existing mapping looks stale. Do not remove the mapping on the tool's suggestion — that silently drops a resource users depend on. See "A mapped resource is reported missing" below.
Read the current bridge guide before editing provider entry points or imports:
- Complete migration: Upgrade a bridged provider from SDKv2 to Plugin Framework.
- Provider retaining both SDKv2 and Plugin Framework resources: Upgrade an SDKv2 provider using mux (rendered documentation).
Do not rely on remembered import paths or API signatures. The guides describe the current bridge release; verify the packages and APIs against the pulumi-terraform-bridge version selected by provider/go.mod before applying the migration.
First look for a public upstream package that constructs the Plugin Framework provider. If upstream exposes the constructor only from an unimportable internal/ package, use skill upstream-patches to add the smallest non-internal shim that returns the upstream provider. This is a legitimate new-patch case; keep the patch limited to exposing the constructor and document when it can be removed.
After applying the appropriate guide and any required patch, run focused provider builds or tests, then rerun upgrade-provider with --no-submit.
A mapped resource is reported missing (do not remove the mapping)
When tfgen finds a token mapping whose resource is no longer in the shimmed provider, it reports the resource as missing and suggests removing the mapping:
Pulumi token "<pulumi-token>" is mapped to TF provider resource "<tf_resource>", but no such resource found. Remove the mapping and try againBefore removing the mapping, verify the removal was real. The same "missing" error appears whether upstream deleted the resource or moved it to the Plugin Framework — an unmuxed SDKv2 bridge can't see a Framework resource — so confirm which happened:
- Inspect the upstream provider at the target tag. If the resource is gone from the SDKv2
ResourcesMapbut a Plugin Framework equivalent now exists (aresource.Resourceimplementation registered in the framework provider'sResourcesmethod), it was migrated, not removed. - Read the upstream CHANGELOG / release notes for the target version. A genuine removal or deprecation is normally announced; a Plugin Framework migration is often described only as an internal refactor.
- Confirm the token really left the generated schema by diffing against the default branch:
default_branch=$(git remote show origin | sed -n 's/.*HEAD branch: //p')
schema_path=$(find provider/cmd -path '*/schema.json' -print -quit)
diff <(git show "origin/${default_branch}:${schema_path}" | jq -r '.resources | keys[]') \
<(jq -r '.resources | keys[]' "$schema_path")Only if it was intentionally removed upstream should you drop the token mapping — and then call out the removal in the upgrade PR so reviewers can plan a deprecation. If it was migrated to the Plugin Framework, keep the resource by muxing the provider (follow the mux guide linked above) so the bridge serves it again; do not remove the mapping.
Upstream provider relies on ignored replace directives
When upgrade-provider fails during Update TF Provider with an upstream module resolution error like:
go get github.com/rancher/...: exit status 1:
go: github.com/rancher/terraform-provider-rancher2@... requires github.com/rancher/rancher@v0.0.0: unknown revision v0.0.0the upstream provider may have invalid-looking require entries that are only made valid by its own replace directives. Go ignores replace directives from dependency modules; only the main module's go.mod replacements are honored.
Fix in the Pulumi provider repo:
- Inspect upstream
go.modat the target tag or commit. - Add the narrowest necessary upstream
replacedirectives toprovider/go.mod. - Avoid copying the entire upstream
replaceblock unless required; broad replacements can conflict with Pulumi or bridge dependencies. - Run
go mod tidyfromprovider/. - Rerun
upgrade-providerfrom the repo root with the target version explicit. Preserve the original major/non-major intent:
upgrade-provider pulumi/<provider> --repo-path . --no-submit --target-version <version>Add --major only when the target upstream version crosses the current upstream major version. Passing --major for a same-major target makes upgrade-provider fail and can trigger unwanted major-version rewrite behavior.
If repo tools are managed by mise, run under the repo environment so Go and converter plugins match CI:
eval "$(mise env)" && upgrade-provider pulumi/<provider> --repo-path . --no-submit --target-version <version>Example: pulumi-rancher2 upgrading to upstream terraform-provider-rancher2 v14.1.0 needed main-module replacements like:
replace (
github.com/rancher/rancher => github.com/rancher/rancher v0.0.0-20260226161459-b186acea1a52
github.com/rancher/rancher/pkg/apis => github.com/rancher/rancher/pkg/apis v0.0.0-20260226161459-b186acea1a52
github.com/rancher/rancher/pkg/client => github.com/rancher/rancher/pkg/client v0.0.0-20260226161459-b186acea1a52
)In that case, copying upstream OpenTelemetry replacements caused conflicts with Pulumi/bridge dependencies; the narrower Rancher-focused replacements were sufficient.
Upstream provider edits vendored dependency but module graph is stale
When make tfgen or upgrade-provider fails compiling the upstream Terraform provider with missing fields or methods from one of its dependencies, check whether upstream edited a vendored copy of that dependency without publishing or requiring a matching module version.
Common symptoms:
unknown field Destination in struct literal of type "github.com/f5devcentral/go-bigip".Gtmmonitor
client.CreateGtmMonitor undefined
client.GetGtmMonitor undefinedConfirm before fixing:
- Identify the dependency package in the compiler errors.
- Inspect the upstream provider tag or commit and compare its
vendor/<module>/...files against the module version selected byprovider/go.mod. - If the needed fields or methods exist only under upstream
vendor/, treat this as an upstream vendored-dependency patch case.
Fix in the Pulumi provider repo:
- Add or update the upstream Terraform provider submodule at
upstream, pinned to the target upstream tag or commit. - Set
.gitmodulesfor the submodule to includeignore = dirtyso an applied patch queue does not leave top-levelgit statusnoisy. - Use the
upstream-patchesskill and./scripts/upstream.sh checkout/check_inworkflow to add a new patch containing only minimalgo.modfiles in the affected vendored dependency directories. This is an allowed new-patch case because the provider needs durable vendored module metadata:
./scripts/upstream.sh checkout
cd upstream
# Add minimal go.mod files under vendor/<module>/...
git add vendor/<module>/go.mod
git commit -m "Add module metadata for vendored <module>"
cd ..
./scripts/upstream.sh check_in- Add narrow
replacedirectives inprovider/go.modthat point only the stale dependency modules at../upstream/vendor/<module>. - Run
go mod tidyfromprovider/, then rerunmake tfgenorupgrade-provider.
Example:
replace github.com/f5devcentral/go-bigip => ../upstream/vendor/github.com/f5devcentral/go-bigip
replace github.com/f5devcentral/go-bigip/f5teem => ../upstream/vendor/github.com/f5devcentral/go-bigip/f5teemAvoid:
- Copying the dependency into
provider/third_partyor another provider-owned vendor directory. - Replacing the whole upstream Terraform provider module with
../upstreamunless necessary; this can break bridge documentation and example discovery because the module cache layout changes. - Hand-editing
patches/*.patchinstead of usingupstream-patches, unless intentionally doing raw patch surgery.
When adding this kind of patch, make the tracking issue explicit and de-duplicated:
- Search existing issues first:
gh issue list --label "area/patch" --search "vendored dependency <module> in:title,body" --json number,title,url- If no matching issue exists, create one with
gh issue create, labeledarea/patch, explaining why the patch exists and when it can be removed:
gh issue create \
--title "Track vendored dependency patch for <module>" \
--label "area/patch" \
--body "$(cat <<'EOF'
The provider upgrade needs a patch because upstream vendors dependency changes
that are not available from the published module graph.
Removal criteria:
- Upstream requires a published dependency module containing the vendored changes, or
- The provider no longer needs the dependency replace directives.
Verification:
- Delete the patch and related provider/go.mod replace directives.
- Run make tfgen.
EOF
)"- Link the issue from the upgrade PR's "Fixes applied to unblock upgrade" section.
Removal criteria should usually be: upstream requires a published dependency module containing the vendored changes, or the provider no longer needs the dependency replace directives. Verify removal by deleting the patch and replace directives, then rerunning make tfgen.
New resources missing module mapping
When new upstream resources appear, token mapping can fail with errors like:
* "google_observability_trace_scope": could not find a module that prefixes 'observability_trace_scope' in '[...]'Fix by updating provider/resources.go (or the repo-equivalent file):
- Determine the Terraform module name (usually the first segment after the provider prefix). In the example, use
observability. - Add a module mapping in
moduleMapping:
var moduleMapping = map[string]string{
"observability": "Observability",
}- If related resources already map to a different module, map the new resource individually instead of adding a new module key. Example:
DataSources: map[string]*tfbridge.DataSourceInfo{
"aws_vpn_gateway": {Tok: awsDataSource(ec2Mod, "getVpnGateway")},
}After updating, rerun upgrade-provider with --no-submit.
.NET duplicate file from nested Get suffix collision
When make generate_sdks fails during .NET SDK generation with an error like:
panic: fatal: An assertion has failed: duplicate file: Chaos/Inputs/ProbeTemplateHttpProbeMethodGetArgs.cslook for a newly-added nested schema type where a parent type and a child field/type collide with the .NET generator's helper suffixes. Common pattern:
- Parent object type:
<X> - Child field/type:
<X>Get - .NET state helper for the parent:
<X>GetArgs.cs - .NET input helper for the child:
<X>GetArgs.cs
Confirm the shape was introduced by the upstream bump before renaming. Compare the generated schema on the default branch with the upgrade branch. Derive the branch and schema path instead of assuming provider-specific names:
default_branch=$(git remote show origin | sed -n 's/.*HEAD branch: //p')
schema_path=$(find provider/cmd -path '*/schema.json' -print -quit)
git show "origin/${default_branch}:${schema_path}" | rg "<NestedTypeName>"
rg "<NestedTypeName>" "$schema_path"Replace <NestedTypeName> with the colliding nested type prefix from the duplicate filename. If rg is unavailable, use grep -n for these literal searches.
Fix by applying a normal bridge Name override in provider/resources.go to rename the smallest nested field that causes the collision. Do not use CSharpName; it only changes C# property labels and does not change generated nested type filenames. Avoid schema post-processors unless there is no ordinary bridge mapping available.
Example:
"harness_chaos_probe_template": {
Tok: harnessResource("chaos", "ProbeTemplate"),
Fields: map[string]*tfbridge.SchemaInfo{
"http_probe": {
Elem: &tfbridge.SchemaInfo{
Fields: map[string]*tfbridge.SchemaInfo{
"method": {
Elem: &tfbridge.SchemaInfo{
Fields: map[string]*tfbridge.SchemaInfo{
"get": {Name: "getMethod"},
},
},
},
},
},
},
},
},After updating the bridge mapping, rerun upgrade-provider --no-submit from the repo root so schema and SDKs regenerate consistently.
ID attribute wrong type (tfgen unresolved ID mapping)
When make tfgen fails with an error like:
error: Resource linode_producer_image_share_group has a problem: "id" attribute is of type "Int", expected type "string". To map this resource consider overriding the SchemaInfo.Type field or specifying ResourceInfo.ComputeID
error: There were 1 unresolved ID mapping errorsThe upstream resource has an id attribute, but it is not a string. Fix it in provider/resources.go (or equivalent) by applying the override:
prov.P.ResourcesMap().Range(func(key string, value shim.Resource) bool {
if value.Schema().Get("id").Type() != shim.TypeString {
r := prov.Resources[key]
if r.Fields == nil {
r.Fields = make(map[string]*tfbridge.SchemaInfo, 1)
}
r.Fields["id"] = &tfbridge.SchemaInfo{Type: "string"}
}
return true
})- If the
idtype is not coercible to string, setResourceInfo.ComputeIDinstead.
ID attribute is input type (tfgen unresolved ID mapping)
When make tfgen fails with an error like:
error: Resource cloudflare_zero_trust_access_ai_controls_mcp_server has a problem: an "id" input attribute is not allowed. To map this resource specify SchemaInfo.Name and ResourceInfo.ComputeIDThe upstream resource exposes id as Optional/Required. Remap the input field to <resource_name>_id and delegate the ID to that new property. Convert the field name to Pulumi camelCase (for example, cloudflare_zero_trust_access_ai_controls_mcp_server -> zeroTrustAccessAiControlsMcpServerId).
"cloudflare_zero_trust_access_ai_controls_mcp_server": {
Fields: map[string]*info.Schema{
"id": {
Name: "zeroTrustAccessAiControlsMcpServerId",
},
},
ComputeID: tfbridge.DelegateIDField(resource.PropertyKey("zeroTrustAccessAiControlsMcpServerId"),
"cloudflare", "https://github.com/pulumi/pulumi-cloudflare"),
},