diff --git a/apps/expo/app/(app)/current-pack/[id].tsx b/apps/expo/app/(app)/current-pack/[id].tsx index 0e0b28f5e3..f5fe095587 100644 --- a/apps/expo/app/(app)/current-pack/[id].tsx +++ b/apps/expo/app/(app)/current-pack/[id].tsx @@ -1,4 +1,3 @@ -import type { PackItem } from '@packrat/types'; import { Avatar, AvatarFallback, @@ -8,6 +7,7 @@ import { } from '@packrat/ui/nativewindui'; import { userStore } from 'expo-app/features/auth/store'; import { usePackDetailsFromStore } from 'expo-app/features/packs/hooks/usePackDetailsFromStore'; +import type { PackItem } from 'expo-app/features/packs/types'; import { type CategorySummary, computeCategorySummaries } from 'expo-app/features/packs/utils'; import { cn } from 'expo-app/lib/cn'; import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme'; @@ -192,10 +192,7 @@ export default function CurrentPackScreen() { index.toString()} - renderItem={(item, index) => ( - // safe-cast: Treaty response type has createdAt?: string but PackItem schema requires string - - )} + renderItem={(item, index) => } /> diff --git a/packages/api/src/routes/catalog/index.ts b/packages/api/src/routes/catalog/index.ts index 1de525fe62..dadd399b1d 100644 --- a/packages/api/src/routes/catalog/index.ts +++ b/packages/api/src/routes/catalog/index.ts @@ -241,17 +241,12 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) async ({ body }) => { const db = createDb(); const data = body; - if (!data.name || data.weight === undefined || data.weight === null || !data.weightUnit) { - return status(400, { error: 'name, weight, and weightUnit are required' }); - } - if (data.weight <= 0) { - return status(400, { error: 'weight must be a positive number' }); - } const { OPENAI_API_KEY, AI_PROVIDER, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_AI_GATEWAY_ID, AI } = getEnv(); if (!OPENAI_API_KEY) { - return status(500, { error: 'OpenAI API key not configured' }); + // Configuration error: surface as a 500 with a clear message + throw new Error('Service unavailable: OpenAI API key not configured'); } const embeddingText = getEmbeddingText(data); @@ -434,14 +429,12 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) throw new NotFoundError('Catalog item not found'); } const data = body; - if (data.weight !== undefined && data.weight !== null && data.weight <= 0) { - return status(400, { error: 'weight must be a positive number' }); - } const { OPENAI_API_KEY, AI_PROVIDER, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_AI_GATEWAY_ID, AI } = getEnv(); if (!OPENAI_API_KEY) { - return status(500, { error: 'OpenAI API key not configured' }); + // Configuration error: surface as a 500 with a clear message + throw new Error('Service unavailable: OpenAI API key not configured'); } const existingItem = await db.query.catalogItems.findFirst({ diff --git a/packages/api/src/routes/weather.ts b/packages/api/src/routes/weather.ts index c6c5af2db1..4bc3c297dd 100644 --- a/packages/api/src/routes/weather.ts +++ b/packages/api/src/routes/weather.ts @@ -11,6 +11,7 @@ import { WeatherSearchQuerySchema, } from '@packrat/schemas/weather'; import { Elysia, status } from 'elysia'; +import { ZodError } from 'zod'; const WEATHER_API_BASE_URL = 'https://api.weatherapi.com/v1'; @@ -153,6 +154,11 @@ export const weatherRoutes = new Elysia({ prefix: '/weather' }) }, }); } catch (error) { + if (error instanceof ZodError) { + const invalidPaths = error.errors.map((e) => e.path.join('.')).join(', '); + console.error('Weather forecast response failed schema validation:', error.errors); + throw new Error(`Weather forecast response failed schema validation at: ${invalidPaths}`); + } console.error('Error fetching weather forecast:', error); return status(500, { error: 'Internal server error', code: 'WEATHER_FORECAST_ERROR' }); } diff --git a/packages/schemas/src/catalog.ts b/packages/schemas/src/catalog.ts index 0b91cafbc8..823daaf3b5 100644 --- a/packages/schemas/src/catalog.ts +++ b/packages/schemas/src/catalog.ts @@ -229,7 +229,7 @@ export const UpdateCatalogItemRequestSchema = z.object({ name: z.string().min(1).max(255).optional(), productUrl: z.string().url().optional(), sku: z.string().optional(), - weight: z.number().optional(), + weight: z.number().positive().optional(), weightUnit: z.enum(WEIGHT_UNITS).optional(), description: z.string().optional(), categories: z.array(z.string()).optional(),