Skip to content

Commit 125d71d

Browse files
alban bertoliniclaude
andcommitted
fix(agent-client): normalize path joining in HttpRequester
When api_endpoint has no trailing slash and action endpoints have no leading slash, the URL concatenation produces broken paths like /backoffice/v1forest/actions/... instead of /backoffice/v1/forest/actions/... Extract a buildUrl() method that ensures the path always starts with / before concatenation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b3b7dcd commit 125d71d

2 files changed

Lines changed: 20 additions & 2 deletions

File tree

packages/agent-client/src/http-requester.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export default class HttpRequester {
3535
contentType?: 'application/json' | 'text/csv';
3636
}): Promise<Data> {
3737
try {
38-
const url = new URL(`${this.baseUrl}${HttpRequester.escapeUrlSlug(path)}`).toString();
38+
const url = this.buildUrl(path);
3939

4040
const req = superagent[method](url)
4141
.timeout(maxTimeAllowed ?? 10_000)
@@ -73,7 +73,7 @@ export default class HttpRequester {
7373
maxTimeAllowed?: number;
7474
stream: WriteStream;
7575
}): Promise<void> {
76-
const url = new URL(`${this.baseUrl}${HttpRequester.escapeUrlSlug(reqPath)}`).toString();
76+
const url = this.buildUrl(reqPath);
7777

7878
return new Promise<void>((resolve, reject) => {
7979
superagent
@@ -97,4 +97,10 @@ export default class HttpRequester {
9797
static escapeUrlSlug(name: string): string {
9898
return encodeURI(name).replace(/([+?*])/g, '\\$1');
9999
}
100+
101+
private buildUrl(path: string): string {
102+
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
103+
104+
return new URL(`${this.baseUrl}${HttpRequester.escapeUrlSlug(normalizedPath)}`).toString();
105+
}
100106
}

packages/agent-client/test/http-requester.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,18 @@ describe('HttpRequester', () => {
160160
});
161161
});
162162

163+
it('should normalize path without leading slash', async () => {
164+
mockRequest.then = jest.fn((onFulfilled: any) => {
165+
return Promise.resolve(onFulfilled({ body: {} }));
166+
});
167+
168+
await requester.query({ method: 'get', path: 'forest/actions/my-action' });
169+
170+
expect(mockSuperagent.get).toHaveBeenCalledWith(
171+
'https://api.example.com/forest/actions/my-action',
172+
);
173+
});
174+
163175
it('should handle URL with prefix', async () => {
164176
const requesterWithPrefix = new HttpRequester('test-token', {
165177
url: 'https://api.example.com',

0 commit comments

Comments
 (0)