Skip to content

fix(miniflare): Fix local scheduled handler returning exception when assets is enabled#13704

Closed
ktKongTong wants to merge 1 commit into
cloudflare:mainfrom
ktKongTong:fix-asset-scheduled
Closed

fix(miniflare): Fix local scheduled handler returning exception when assets is enabled#13704
ktKongTong wants to merge 1 commit into
cloudflare:mainfrom
ktKongTong:fix-asset-scheduled

Conversation

@ktKongTong

@ktKongTong ktKongTong commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Fixes #9882

add scheduled handler for asset's rpc-proxy worker


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because:

A picture of a cute animal (not mandatory, but encouraged)


Open in Devin Review

@changeset-bot

changeset-bot Bot commented Apr 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5990537

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@workers-devprod workers-devprod requested review from a team and ascorbic and removed request for a team April 28, 2026 06:59
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • packages/miniflare/src/workers/assets/rpc-proxy.worker.ts: [@cloudflare/wrangler]
  • packages/miniflare/test/index.spec.ts: [@cloudflare/wrangler]

@ktKongTong ktKongTong changed the title fix(miniflare): Fix local scheduled handler dispatch for Workers + As… fix(miniflare): Fix local scheduled handler returning exception when assets is enabled Apr 28, 2026

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 3 additional findings in Devin Review.

Open in Devin Review

Comment on lines +29 to +34
async scheduled(controller: ScheduledController) {
await this.env.USER_WORKER.scheduled({
scheduledTime: new Date(controller.scheduledTime),
cron: controller.cron,
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 RPCProxyWorker discards scheduled outcome, silently swallowing user worker errors

The scheduled handler calls this.env.USER_WORKER.scheduled(...) but ignores the returned FetcherScheduledResult. The Fetcher.scheduled() API doesn't throw on handler failures — it returns { outcome: "exception" } (as evidenced by packages/miniflare/src/workers/core/scheduled.ts:14-15 which checks result.outcome). Because the RPCProxyWorker's handler completes without throwing, workerd reports the outer dispatch as { outcome: "ok" }. This means when assets are enabled and the user worker's scheduled handler throws an exception, handleScheduled will return a 200 "ok" response instead of a 500 "exception" response.

Suggested change
async scheduled(controller: ScheduledController) {
await this.env.USER_WORKER.scheduled({
scheduledTime: new Date(controller.scheduledTime),
cron: controller.cron,
});
}
async scheduled(controller: ScheduledController) {
const result = await this.env.USER_WORKER.scheduled({
scheduledTime: new Date(controller.scheduledTime),
cron: controller.cron,
});
if (result.outcome !== "ok") {
throw new Error(`User worker scheduled handler returned: ${result.outcome}`);
}
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@pkg-pr-new

pkg-pr-new Bot commented May 1, 2026

Copy link
Copy Markdown
create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@13704

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@13704

miniflare

npm i https://pkg.pr.new/miniflare@13704

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@13704

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@13704

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@13704

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@13704

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@13704

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@13704

wrangler

npm i https://pkg.pr.new/wrangler@13704

commit: 5990537

@petebacondarwin petebacondarwin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the best fix? It feels like there should be a more general purpose solution that would cover all the other non-fetch entry-points too.

fetch() {
return new Response(scheduledRun);
},
scheduled() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should check that we are passing through the correct parameters here.

@github-project-automation github-project-automation Bot moved this from Untriaged to In Review in workers-sdk May 3, 2026
@petebacondarwin petebacondarwin marked this pull request as draft June 1, 2026 19:21
@petebacondarwin

Copy link
Copy Markdown
Contributor

@ktKongTong - would you like to work on this PR further?

@ktKongTong

Copy link
Copy Markdown
Contributor Author

@petebacondarwin Thanks for the ping. I’d still like to continue with this PR. Sorry for the delay. I’ll take another look and update the PR.

@ktKongTong

ktKongTong commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

@petebacondarwin

I spent some time testing this against the current implementation. The simple forwarding approach in this PR does trigger the user worker’s scheduled() handler, but it doesn’t behave like a transparent scheduled dispatch. Since the call goes through the assets RPC proxy, controller.noRetry() called in the user script is not reflected in the rpc-proxy worker’s outer scheduled result immediately.

// user worker
//...
scheduled(controller) {
  controller.noRetry();
}

the ?format=json(#14079) result:

// expect
{
"outcome": "ok",
"noRetry": true
}
//actual
{
"outcome": "ok",
"noRetry": false
}

I can only observe the expected result if the proxy explicitly yield back to the runtime, which feels fragile.

async scheduled(controller: ScheduledController) {
	await this.env.USER_WORKER.scheduled({
		cron: controller.cron,
		scheduledTime: new Date(controller.scheduledTime),
		noRetry: () => controller.noRetry(),
	});
	await new Promise<void>((resolve) => { setTimeout(resolve, 0) })
}

so I create another PR: #14177, which bypass the assets' rpc-proxy worker for scheduled handler.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

can't test cron triggers locally with assets or redirected config

3 participants