Files
dotfiles/dot_claude/skills/cloudflare/references/pulumi/gotchas.md
T
Prad NukalaandClaude Fable 5 20891f3910 config(git,claude,ssh): templated gitconfig; claude settings/commands/hooks/skills; ssh config
gitconfig: name/email from chezmoi data, gh credential helper by PATH
lookup (portable to darwin). claude: no credentials, history, projects,
sessions, or plugin caches. ssh config as private_ (0600).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 14:53:46 -04:00

6.4 KiB

Troubleshooting & Best Practices

Common Errors

"No bundler/build step" - Pulumi uploads raw code

Problem: Worker fails with "Cannot use import statement outside a module"
Cause: Pulumi doesn't bundle Worker code - uploads exactly what you provide
Solution: Build Worker BEFORE Pulumi deploy

// WRONG: Pulumi won't bundle this
const worker = new cloudflare.WorkerScript("worker", {
    content: fs.readFileSync("./src/index.ts", "utf8"), // Raw TS file
});

// RIGHT: Build first, then deploy
import * as command from "@pulumi/command";
const build = new command.local.Command("build", {
    create: "npm run build",
    dir: "./worker",
});
const worker = new cloudflare.WorkerScript("worker", {
    content: build.stdout.apply(() => fs.readFileSync("./worker/dist/index.js", "utf8")),
}, {dependsOn: [build]});

"wrangler.toml not consumed" - Config drift

Problem: Local wrangler dev works, Pulumi deploy fails
Cause: Pulumi ignores wrangler.toml - must duplicate config
Solution: Generate wrangler.toml from Pulumi or keep synced manually

// Pattern: Export Pulumi config to wrangler.toml
const workerConfig = {
    name: "my-worker",
    compatibilityDate: "2025-01-01",
    compatibilityFlags: ["nodejs_compat"],
};

new command.local.Command("generate-wrangler", {
    create: pulumi.interpolate`cat > wrangler.toml <<EOF
name = "${workerConfig.name}"
compatibility_date = "${workerConfig.compatibilityDate}"
compatibility_flags = ${JSON.stringify(workerConfig.compatibilityFlags)}
EOF`,
});

"False no-changes detection" - Content SHA unchanged

Problem: Worker code updated, Pulumi says "no changes"
Cause: Content hash identical (whitespace/comment-only change)
Solution: Add build timestamp or version to force update

const version = Date.now().toString();
const worker = new cloudflare.WorkerScript("worker", {
    content: code,
    plainTextBindings: [{name: "VERSION", text: version}], // Forces new deployment
});

"D1 migrations don't run on pulumi up"

Problem: Database schema not applied after D1 database created
Cause: Pulumi creates database but doesn't run migrations
Solution: Use Command resource with dependsOn

const db = new cloudflare.D1Database("db", {accountId, name: "mydb"});

// Run migrations after DB created
const migration = new command.local.Command("migrate", {
    create: pulumi.interpolate`wrangler d1 execute ${db.name} --file ./schema.sql`,
}, {dependsOn: [db]});

// Worker depends on migrations
const worker = new cloudflare.WorkerScript("worker", {
    d1DatabaseBindings: [{name: "DB", databaseId: db.id}],
}, {dependsOn: [migration]});

"Missing required property 'accountId'"

Problem: Error: Missing required property 'accountId'
Cause: Account ID not provided in resource configuration
Solution: Add to stack config

# Pulumi.<stack>.yaml
config:
  cloudflare:accountId: "abc123..."

"Binding name mismatch"

Problem: Worker fails with "env.MY_KV is undefined"
Cause: Binding name in Pulumi != name in Worker code
Solution: Match exactly (case-sensitive)

// Pulumi
kvNamespaceBindings: [{name: "MY_KV", namespaceId: kv.id}]

// Worker code
export default { async fetch(request, env) { await env.MY_KV.get("key"); }}

"API token permissions insufficient"

Problem: Error: authentication error (10000)
Cause: Token lacks required permissions
Solution: Grant token permissions: Account.Workers Scripts:Edit, Account.Account Settings:Read

"Resource not found after import"

Problem: Imported resource shows as changed on next pulumi up
Cause: State mismatch between actual resource and Pulumi config
Solution: Check property names/types match exactly

pulumi import cloudflare:index/workerScript:WorkerScript my-worker <account_id>/<worker_name>
pulumi preview # If shows changes, adjust Pulumi code to match actual resource

"v6.x Worker versioning confusion"

Problem: Worker deployed but not receiving traffic
Cause: v6.x requires Worker + WorkerVersion + WorkersDeployment (3 resources)
Solution: Use WorkerScript (auto-versioning) OR full versioning pattern

// SIMPLE: WorkerScript auto-versions (default behavior)
const worker = new cloudflare.WorkerScript("worker", {
    accountId, name: "my-worker", content: code,
});

// ADVANCED: Manual versioning for gradual rollouts (v6.x)
const worker = new cloudflare.Worker("worker", {accountId, name: "my-worker"});
const version = new cloudflare.WorkerVersion("v1", {
    accountId, workerId: worker.id, content: code, compatibilityDate: "2025-01-01",
});
const deployment = new cloudflare.WorkersDeployment("prod", {
    accountId, workerId: worker.id, versionId: version.id,
});

Best Practices

  1. Always set compatibilityDate - Locks Worker behavior, prevents breaking changes
  2. Build before deploy - Pulumi doesn't bundle; use Command resource or CI build step
  3. Match binding names - Case-sensitive, must match between Pulumi and Worker code
  4. Use dependsOn for migrations - Ensure D1 migrations run before Worker deploys
  5. Version Worker content - Add VERSION binding to force redeployment on content changes
  6. Store secrets in stack config - Use pulumi config set --secret for API keys

Limits

Resource Limit Notes
Worker script size 10 MB Includes all dependencies, after compression
Worker CPU time 10ms (free), 30s default / 5min max (paid) Per request
KV keys per namespace Unlimited 1000 ops/sec write, 100k ops/sec read
R2 storage Unlimited Class A ops: 1M/mo free, Class B: 10M/mo free
D1 databases 50,000 per account Free: 10 per account, 5 GB each
Queues 10,000 per account Free: 1M ops/day
Pages projects 500 per account Free: 100 projects
API requests Varies by plan ~1200 req/5min on free

Resources


See: README.md, configuration.md, api.md, patterns.md