Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[D1] Add user friendly D1 validation error messages for dev --remote and deploy #4914

Merged
merged 9 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fair-shoes-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": patch
---

add user friendly error messages for d1 validation errors
nora-soderlund marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 9 additions & 0 deletions packages/wrangler/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,15 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
) {
err.preventReport();

if (
err.notes[0].text ===
"binding DB of type d1 must have a valid `id` specified [code: 10021]"
) {
throw new UserError(
"You must use a real database in the database_id configuration. You can find your databases using 'wrangler d1 list', or read how to develop locally with D1 here: https://developers.cloudflare.com/d1/configuration/local-development"
);
}

const maybeNameToFilePath = (moduleName: string) => {
// If this is a service worker, always return the entrypoint path.
// Service workers can't have additional JavaScript modules.
Expand Down
98 changes: 68 additions & 30 deletions packages/wrangler/src/dev/remote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
CfWorkerContext,
CfWorkerInit,
} from "../deployment-bundle/worker";
import type { ParseError } from "../parse";
import type { AssetPaths } from "../sites";
import type { ChooseAccountItem } from "../user";
import type {
Expand Down Expand Up @@ -336,27 +337,18 @@ export function useWorker(
start().catch((err) => {
// we want to log the error, but not end the process
// since it could recover after the developer fixes whatever's wrong
if ((err as { code: string }).code !== "ABORT_ERR") {
// instead of logging the raw API error to the user,
// give them friendly instructions
// for error 10063 (workers.dev subdomain required)
if (err.code === 10063) {
const errorMessage =
"Error: You need to register a workers.dev subdomain before running the dev command in remote mode";
const solutionMessage =
"You can either enable local mode by pressing l, or register a workers.dev subdomain here:";
const onboardingLink = `https://dash.cloudflare.com/${props.accountId}/workers/onboarding`;
logger.error(
`${errorMessage}\n${solutionMessage}\n${onboardingLink}`
);
} else if (err.code === 10049) {
// instead of logging the raw API error to the user,
// give them friendly instructions
if ((err as unknown as { code: string }).code !== "ABORT_ERR") {
// code 10049 happens when the preview token expires
if (err.code === 10049) {
logger.log("Preview token expired, fetching a new one");
// code 10049 happens when the preview token expires

// since we want a new preview token when this happens,
// lets increment the counter, and trigger a rerun of
// the useEffect above
setRestartCounter((prevCount) => prevCount + 1);
} else {
} else if (!handleUserFriendlyError(err, props.accountId)) {
logger.error("Error on remote worker:", err);
}
}
Expand Down Expand Up @@ -525,21 +517,15 @@ export async function getRemotePreviewToken(props: RemoteProps) {
return workerPreviewToken;
}
return start().catch((err) => {
if ((err as { code?: string })?.code !== "ABORT_ERR") {
// instead of logging the raw API error to the user,
// give them friendly instructions
// for error 10063 (workers.dev subdomain required)
if (err?.code === 10063) {
const errorMessage =
"Error: You need to register a workers.dev subdomain before running the dev command in remote mode";
const solutionMessage =
"You can either enable local mode by pressing l, or register a workers.dev subdomain here:";
const onboardingLink = `https://dash.cloudflare.com/${props.accountId}/workers/onboarding`;
logger.error(`${errorMessage}\n${solutionMessage}\n${onboardingLink}`);
} else if (err?.code === 10049) {
// code 10049 happens when the preview token expires
// we want to log the error, but not end the process
// since it could recover after the developer fixes whatever's wrong
// instead of logging the raw API error to the user,
// give them friendly instructions
if ((err as unknown as { code: string })?.code !== "ABORT_ERR") {
// code 10049 happens when the preview token expires
if (err.code === 10049) {
logger.log("Preview token expired, restart server to fetch a new one");
} else {
} else if (!handleUserFriendlyError(err, props.accountId)) {
helpIfErrorIsSizeOrScriptStartup(err, props.bundle?.dependencies || {});
logger.error("Error on remote worker:", err);
}
Expand Down Expand Up @@ -684,3 +670,55 @@ function ChooseAccount(props: {
</>
);
}

/**
* A switch for handling thrown error mappings to user friendly
* messages, does not perform any logic other than logging errors.
* @returns if the error was handled or not
*/
function handleUserFriendlyError(error: ParseError, accountId?: string) {
switch ((error as unknown as { code: number }).code) {
// code 10021 is a validation error
case 10021: {
// if it is the following message, give a more user friendly
// error, otherwise do not handle this error in this function
if (
error.notes[0].text ===
"binding DB of type d1 must have a valid `id` specified [code: 10021]"
) {
const errorMessage =
"Error: You must use a real database in the preview_database_id configuration.";
const solutionMessage =
"You can find your databases using 'wrangler d1 list', or read how to develop locally with D1 here:";
const documentationLink = `https://developers.cloudflare.com/d1/configuration/local-development`;

logger.error(
`${errorMessage}\n${solutionMessage}\n${documentationLink}`
);

return true;
}

return false;
}

// for error 10063 (workers.dev subdomain required)
case 10063: {
const errorMessage =
"Error: You need to register a workers.dev subdomain before running the dev command in remote mode";
const solutionMessage =
"You can either enable local mode by pressing l, or register a workers.dev subdomain here:";
const onboardingLink = accountId
? `https://dash.cloudflare.com/${accountId}/workers/onboarding`
: "https://dash.cloudflare.com/?to=/:account/workers/onboarding";

logger.error(`${errorMessage}\n${solutionMessage}\n${onboardingLink}`);

return true;
}

default: {
return false;
}
}
}
Loading