From 5ea7342f4eb80c41d13da0fc8858c03310a588d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Fri, 1 May 2026 10:29:43 +0200 Subject: [PATCH] Send Wahoo route file as data URI, not raw base64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wahoo's POST /v1/routes expects route[file] as 'data:application/vnd.fit;base64,'. We were sending raw base64, which Wahoo silently discarded — the route would appear in the user's list with metadata (distance, ascent come from request fields) but file.url stayed null and the Wahoo app rendered no track. Confirmed by re-fetching route 50197876 via GET /v1/routes/:id. Co-Authored-By: Claude Opus 4.7 --- apps/journal/app/lib/sync/providers/wahoo.test.ts | 4 +++- apps/journal/app/lib/sync/providers/wahoo.ts | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/journal/app/lib/sync/providers/wahoo.test.ts b/apps/journal/app/lib/sync/providers/wahoo.test.ts index 74b33d3..25d90e7 100644 --- a/apps/journal/app/lib/sync/providers/wahoo.test.ts +++ b/apps/journal/app/lib/sync/providers/wahoo.test.ts @@ -114,7 +114,9 @@ describe("wahooProvider.pushRoute", () => { expect(body.get("route[name]")).toBe("Test route"); expect(body.get("route[description]")).toBe("A test"); expect(body.get("route[start_lat]")).toBe("52.52"); - expect(body.get("route[file]")).toBe(Buffer.from(fit).toString("base64")); + expect(body.get("route[file]")).toBe( + `data:application/vnd.fit;base64,${Buffer.from(fit).toString("base64")}`, + ); }); it.each([ diff --git a/apps/journal/app/lib/sync/providers/wahoo.ts b/apps/journal/app/lib/sync/providers/wahoo.ts index deb0a8e..7f7ce64 100644 --- a/apps/journal/app/lib/sync/providers/wahoo.ts +++ b/apps/journal/app/lib/sync/providers/wahoo.ts @@ -181,7 +181,10 @@ export const wahooProvider: SyncProvider = { }, async pushRoute(tokens: TokenSet, payload: PushRoutePayload): Promise { - const fitBase64 = Buffer.from(payload.fit).toString("base64"); + // Wahoo expects route[file] as a data URI, not raw base64. Sending plain + // base64 results in a route record where file.url is null and the Wahoo + // app shows the route in the list (with metadata) but renders no track. + const fitDataUri = `data:application/vnd.fit;base64,${Buffer.from(payload.fit).toString("base64")}`; const body = new URLSearchParams({ "route[external_id]": payload.externalId, "route[provider_updated_at]": payload.providerUpdatedAt.toISOString(), @@ -191,7 +194,7 @@ export const wahooProvider: SyncProvider = { "route[start_lng]": payload.startLng.toString(), "route[distance]": payload.distance.toString(), "route[ascent]": payload.ascent.toString(), - "route[file]": fitBase64, + "route[file]": fitDataUri, }); if (payload.description) body.set("route[description]", payload.description); if (payload.filename) body.set("route[filename]", payload.filename);