Help: Binding undefined during build process

I'm creating a rag app entirely with cloudflare and next-on-pages and hono as api server however, when i try to access the binding, it gives this error.
[wrangler:inf] GET /chat 200 OK (21ms)
[wrangler:inf] GET /_next/static/chunks/808-024d9e9f46b0d710.js 200 OK (7ms)
✘ [ERROR] Error creating context: TypeError: Cannot destructure property 'AI' of 'e.env' as it is undefined.

✘ [ERROR] Environment not found
[wrangler:inf] POST /api/context/text 500 Internal Server Error (79ms)
[wrangler:inf] GET /chat 200 OK (21ms)
[wrangler:inf] GET /_next/static/chunks/808-024d9e9f46b0d710.js 200 OK (7ms)
✘ [ERROR] Error creating context: TypeError: Cannot destructure property 'AI' of 'e.env' as it is undefined.

✘ [ERROR] Environment not found
[wrangler:inf] POST /api/context/text 500 Internal Server Error (79ms)
heres the code i'm using
const app = new Hono<{ Bindings: CloudflareEnv }>();
//`CloudflareEnv` is generated by running the command cf-typegen

app.post("/", zValidator("json", textContextSchema), async (c) => {
..
const { AI, VECTORIZE } = c.env;
if (c.env) {
// Debugging
console.log("Environment Bindings: ", c.env);
console.log("AI Binding: ", AI);
console.log("VECTORIZE Binding: ", VECTORIZE);

const embeddingResult = await c.env.AI.run(
"@cf/baai/bge-base-en-v1.5",
{
text: content,
},
);

const embedding = embeddingResult.data[0];

// 6. Save the embeddings into Vectorize
if (embedding && embedding.length > 0) {
await c.env.VECTORIZE.insert([
{
id: GenerateUUID(),
values: embedding,
metadata: { text: content, context_id: contextId },
},
]);
} else {
console.error("Invalid embedding:", embedding);
}
} else {
console.error("Environment not found");
return c.json({ error: "Environment bindings inaccessible" }, 500);
}
...
const app = new Hono<{ Bindings: CloudflareEnv }>();
//`CloudflareEnv` is generated by running the command cf-typegen

app.post("/", zValidator("json", textContextSchema), async (c) => {
..
const { AI, VECTORIZE } = c.env;
if (c.env) {
// Debugging
console.log("Environment Bindings: ", c.env);
console.log("AI Binding: ", AI);
console.log("VECTORIZE Binding: ", VECTORIZE);

const embeddingResult = await c.env.AI.run(
"@cf/baai/bge-base-en-v1.5",
{
text: content,
},
);

const embedding = embeddingResult.data[0];

// 6. Save the embeddings into Vectorize
if (embedding && embedding.length > 0) {
await c.env.VECTORIZE.insert([
{
id: GenerateUUID(),
values: embedding,
metadata: { text: content, context_id: contextId },
},
]);
} else {
console.error("Invalid embedding:", embedding);
}
} else {
console.error("Environment not found");
return c.json({ error: "Environment bindings inaccessible" }, 500);
}
...
this block always fails my wrangler-toml
[ai]
binding = "AI"

[[vectorize]]
binding = "VECTORIZE"
index_name = "dropbase-index"
[ai]
binding = "AI"

[[vectorize]]
binding = "VECTORIZE"
index_name = "dropbase-index"
8 Replies
danim47c
danim47c2d ago
Hello, I imagine you are mounting next-on-pages in a Hono route? at first view it seems to be strange, could I see you next.config.js and your package.json as well?
Dev
DevOP18h ago
sure, >package.json
{
"name": "dropbase",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "npm run format && next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"format": "count=$(prettier --write . --log-level silent | grep -v '(unchanged)' | wc -l); echo \"$count files formatted\"",
"check": "npm run lint && npm run format:check",
"format:check": "prettier --check .",
"pages:build": "bunx @cloudflare/next-on-pages",
"preview": "bun pages:build && wrangler pages dev --experimental-vectorize-bind-to-prod --ai",
"deploy": "bun pages:build && wrangler pages deploy",
"cf-typegen": "wrangler types --env-interface CloudflareEnv env.d.ts",
"generate": "drizzle-kit generate",
"migrate": "wrangler d1 migrations apply ragchat --remote",
"studio": "drizzle-kit studio"
},
"dependencies": {
"@cloudflare/ai": "^1.2.2",
"@hono/zod-validator": "^0.4.2",
"@hookform/resolvers": "^3.10.0",
"@phosphor-icons/react": "^2.1.7",
"@radix-ui/react-dialog": "^1.1.5",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-popover": "^1.1.5",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.6",
"@t3-oss/env-nextjs": "^0.11.1",
"better-auth": "^1.1.14",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"drizzle-orm": "^0.38.4",
"hono": "^4.6.17",
"lucide-react": "^0.473.0",
"next": "15.1.4",
"next-themes": "^0.4.4",
"prettier": "^3.4.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"sonner": "^1.7.2",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.1"
},

{
"name": "dropbase",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "npm run format && next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"format": "count=$(prettier --write . --log-level silent | grep -v '(unchanged)' | wc -l); echo \"$count files formatted\"",
"check": "npm run lint && npm run format:check",
"format:check": "prettier --check .",
"pages:build": "bunx @cloudflare/next-on-pages",
"preview": "bun pages:build && wrangler pages dev --experimental-vectorize-bind-to-prod --ai",
"deploy": "bun pages:build && wrangler pages deploy",
"cf-typegen": "wrangler types --env-interface CloudflareEnv env.d.ts",
"generate": "drizzle-kit generate",
"migrate": "wrangler d1 migrations apply ragchat --remote",
"studio": "drizzle-kit studio"
},
"dependencies": {
"@cloudflare/ai": "^1.2.2",
"@hono/zod-validator": "^0.4.2",
"@hookform/resolvers": "^3.10.0",
"@phosphor-icons/react": "^2.1.7",
"@radix-ui/react-dialog": "^1.1.5",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-popover": "^1.1.5",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.6",
"@t3-oss/env-nextjs": "^0.11.1",
"better-auth": "^1.1.14",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"drizzle-orm": "^0.38.4",
"hono": "^4.6.17",
"lucide-react": "^0.473.0",
"next": "15.1.4",
"next-themes": "^0.4.4",
"prettier": "^3.4.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"sonner": "^1.7.2",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.1"
},

"devDependencies": {
"@cloudflare/next-on-pages": "1",
"@cloudflare/workers-types": "^4.20250109.0",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"drizzle-kit": "^0.30.2",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5",
"vercel": "^39.3.0",
"wrangler": "^3.103.2"
}
"devDependencies": {
"@cloudflare/next-on-pages": "1",
"@cloudflare/workers-types": "^4.20250109.0",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"drizzle-kit": "^0.30.2",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5",
"vercel": "^39.3.0",
"wrangler": "^3.103.2"
}
>next.config.ts
import type { NextConfig } from "next";
import { setupDevPlatform } from "@cloudflare/next-on-pages/next-dev";
import { fileURLToPath } from "node:url";
import createJiti from "jiti";

const jiti = createJiti(fileURLToPath(import.meta.url));
jiti("./src/env.ts");

const nextConfig: NextConfig = {
experimental: {
serverActions: {
bodySizeLimit: "2mb",
},
},
};

if (process.env.NODE_ENV === "development") {
(async () => {
await setupDevPlatform({
persist: true,
});
})();
}

export default nextConfig;
import type { NextConfig } from "next";
import { setupDevPlatform } from "@cloudflare/next-on-pages/next-dev";
import { fileURLToPath } from "node:url";
import createJiti from "jiti";

const jiti = createJiti(fileURLToPath(import.meta.url));
jiti("./src/env.ts");

const nextConfig: NextConfig = {
experimental: {
serverActions: {
bodySizeLimit: "2mb",
},
},
};

if (process.env.NODE_ENV === "development") {
(async () => {
await setupDevPlatform({
persist: true,
});
})();
}

export default nextConfig;
how do i do that? i followed a tutorial on integrating hono in nextjs >src/app/api/[...route]/route.ts
import { Hono } from "hono";
import { handle } from "hono/vercel";
import hello from "./hello";
import chat from "./chat";
import textContext from "./context/text";

export const runtime = "edge";

const app = new Hono().basePath("/api");

const routes = app
.route("/hello", hello)
.route("/chat", chat)
.route("/context/text", textContext);

export const GET = handle(app);
export const POST = handle(app);

export type AppType = typeof routes;
import { Hono } from "hono";
import { handle } from "hono/vercel";
import hello from "./hello";
import chat from "./chat";
import textContext from "./context/text";

export const runtime = "edge";

const app = new Hono().basePath("/api");

const routes = app
.route("/hello", hello)
.route("/chat", chat)
.route("/context/text", textContext);

export const GET = handle(app);
export const POST = handle(app);

export type AppType = typeof routes;
>src/lib/hono-client.ts
import { hc } from "hono/client";
import { AppType } from "@/app/api/[...route]/route";
import { env } from "@/env";

export const client = hc<AppType>(env.NEXT_PUBLIC_BASE_URL!);
import { hc } from "hono/client";
import { AppType } from "@/app/api/[...route]/route";
import { env } from "@/env";

export const client = hc<AppType>(env.NEXT_PUBLIC_BASE_URL!);
danim47c
danim47c18h ago
Okey, undersand, so you have two separate packages, the one with a worker with hono and the one with next-on-pages in that case you should have two different wrangler.toml files do you?
Dev
DevOP16h ago
these ones : "@cloudflare/next-on-pages": "1", "@cloudflare/workers-types": "^4.20250109.0", ?? and no, i only have one wrangler.toml file
danim47c
danim47c9h ago
Okey, that is the problem Cloudflare workers and pages are separate things, you must keep them in separate packages you were probably trying to access a worker's binding from the page
danim47c
danim47c9h ago
Cloudflare Docs
Functions · Cloudflare Pages docs
Pages Functions allows you to build full-stack applications by executing code on the Cloudflare network with Cloudflare Workers. With Functions, you can introduce application aspects such as authenticating, handling form submissions, or working with middleware. Workers runtime features are configurable on Pages Functions, including compatibility...
Dev
DevOP8h ago
ohh, got it, i'll check it out, thanks
danim47c
danim47c7h ago
Great, please, ask for help if needed

Did you find this page helpful?