Skip to content

fix: raise find-my-way's default maxParamLength so /base64/:value stops 404ing on longer payloads#160

Closed
cyfung1031 wants to merge 2 commits into
jaredwray:mainfrom
cyfung1031:fix/base64-max-param-length
Closed

fix: raise find-my-way's default maxParamLength so /base64/:value stops 404ing on longer payloads#160
cyfung1031 wants to merge 2 commits into
jaredwray:mainfrom
cyfung1031:fix/base64-max-param-length

Conversation

@cyfung1031

@cyfung1031 cyfung1031 commented Jul 17, 2026

Copy link
Copy Markdown

Please check if the PR fulfills these requirements

  • Followed the Contributing and Code of Conduct guidelines.
  • Tests for the changes have been added (for bug fixes/features) with 100% code coverage.

What kind of change does this PR introduce? Bug fix — /base64/:value 404s on payloads longer than Fastify's default 100-character route-param limit.


Summary

find-my-way (Fastify's router) defaults maxParamLength to 100 characters. Any single-segment route param longer than that silently fails to match in the router and the request gets a 404 instead of reaching the route handler — most noticeably on /base64/:value, where any realistically sized base64 payload (more than ~75 raw bytes / ~100 encoded characters) returns 404 instead of being decoded.

I ran into this while migrating a project's test fixtures from another httpbin-style mock off of mockhttp.org, and initially suspected it was a hosting/CDN-level limit rather than something in this repo. I confirmed it isn't: I reproduced the exact same 100/101-character boundary locally against an unmodified checkout of main, so this is Fastify's own router configuration, not an infra limit.

$ curl -s -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:3000/base64/$(python3 -c "print('A'*99+'=')")"
200
$ curl -s -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:3000/base64/$(python3 -c "print('A'*100+'=')")"
404

Fix

Passes routerOptions.maxParamLength through getFastifyConfig(), raising it to 8192. I deliberately used the current routerOptions.maxParamLength config path rather than the top-level maxParamLength option — the latter is deprecated in this Fastify version and logs a FSTDEP022 warning on every boot.

Test plan

  • Added a regression test in test/mock-http.test.ts that spins up a real MockHttp server and confirms a >100-char base64 payload round-trips correctly (this is the level that would have caught the bug — the existing test/routes/dynamic-data/base64.test.ts unit tests construct their own bare Fastify() instance without getFastifyConfig(), so they can't exercise this).
  • Added a unit test in test/fastify-config.test.ts asserting the config raises maxParamLength past the 100-char default.
  • pnpm test (lint + full vitest run with coverage) passes locally: 475 tests, 100% line coverage, no lint warnings.
  • Manually verified against a local tsx src/index.ts instance that the previously-404ing payload now returns 200 with the correct decoded body, and that the fix doesn't loosen the limit unboundedly (a 9000-char param still correctly 404s).

…ps 404ing on longer payloads

find-my-way (Fastify's router) defaults maxParamLength to 100
characters. Any single-segment route param longer than that silently
fails to match and returns 404 instead of reaching the route handler
- most noticeably on /base64/:value, where any realistically sized
encoded payload (more than ~75 raw bytes) 404s instead of decoding.

Reproduced against the deployed instance (mockhttp.org) by bisecting
the exact boundary (100 chars OK, 101 chars 404) and confirmed the
same behavior locally with an unmodified checkout, so this isn't a
hosting/CDN limit - it's Fastify's own router configuration.

Fixes by passing routerOptions.maxParamLength through
getFastifyConfig() (the deprecated top-level maxParamLength option
triggers a Fastify deprecation warning, so this uses the current
router-scoped option instead).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request increases Fastify's maxParamLength router option to 8192 to prevent silent 404 errors on long single-segment parameters, such as base64 payloads, and adds corresponding unit and integration tests. The feedback suggests wrapping the new integration test in a try...finally block to ensure the mock server is properly closed even if assertions fail, preventing potential port conflicts or hanging test runs.

Comment thread test/mock-http.test.ts
Comment on lines +117 to +138
test("should route single-segment params longer than find-my-way's 100-char default", async () => {
const mock = new MockHttp({ logging: false });
await mock.start();

// "The quick brown fox..." (338 chars) base64-encoded, well past the
// find-my-way default maxParamLength of 100.
const longValue =
"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4gVGhpcyBzZW50ZW5jZSBjb250YWlucyBldmVyeSBsZXR0ZXIgb2YgdGhlIEVuZ2xpc2ggYWxwaGFiZXQgYW5kIGlzIG9mdGVuIHVzZWQgZm9yIHR5cGluZyBwcmFjdGljZSwgZm9udCB0ZXN0aW5nLCBhbmQgZW5jb2RpbmcgZXhwZXJpbWVudHMuIEJhc2U2NCBlbmNvZGluZyB0cmFuc2Zvcm1zIHRoaXMgcmVhZGFibGUgdGV4dCBpbnRvIGEgc2VxdWVuY2Ugb2YgQVNDSUkgY2hhcmFjdGVycyB0aGF0IGNhbiBzYWZlbHkgYmUgdHJhbnNtaXR0ZWQgb3Igc3RvcmVkIGluIHN5c3RlbXMgdGhhdCBoYW5kbGUgdGV4dC1vbmx5IGRhdGEu";
expect(longValue.length).toBeGreaterThan(100);

const response = await mock.server.inject({
method: "GET",
url: `/base64/${longValue}`,
});

expect(response.statusCode).toBe(200);
expect(response.payload).toBe(
"The quick brown fox jumps over the lazy dog. This sentence contains every letter of the English alphabet and is often used for typing practice, font testing, and encoding experiments. Base64 encoding transforms this readable text into a sequence of ASCII characters that can safely be transmitted or stored in systems that handle text-only data.",
);

await mock.close();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If an assertion fails during the test execution, await mock.close() will not be reached, leaving the server running and the port bound. This can cause subsequent tests to fail due to port conflicts or cause the test runner to hang. Wrapping the test logic in a try...finally block ensures that the server is always properly closed.

	test("should route single-segment params longer than find-my-way's 100-char default", async () => {
		const mock = new MockHttp({ logging: false });
		await mock.start();

		try {
			// "The quick brown fox..." (338 chars) base64-encoded, well past the
			// find-my-way default maxParamLength of 100.
			const longValue =
				"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4gVGhpcyBzZW50ZW5jZSBjb250YWlucyBldmVyeSBsZXR0ZXIgb2YgdGhlIEVuZ2xpc2ggYWxwaGFiZXQgYW5kIGlzIG9mdGVuIHVzZWQgZm9yIHR5cGluZyBwcmFjdGljZSwgZm9udCB0ZXN0aW5nLCBhbmQgZW5jb2RpbmcgZXhwZXJpbWVudHMuIEJhc2U2NCBlbmNvZGluZyB0cmFuc2Zvcm1zIHRoaXMgcmVhZGFibGUgdGV4dCBpbnRvIGEgc2VxdWVuY2Ugb2YgQVNDSUkgY2hhcmFjdGVycyB0aGF0IGNhbiBzYWZlbHkgYmUgdHJhbnNtaXR0ZWQgb3Igc3RvcmVkIGluIHN5c3RlbXMgdGhhdCBoYW5kbGUgdGV4dC1vbmx5IGRhdGEu";
			expect(longValue.length).toBeGreaterThan(100);

			const response = await mock.server.inject({
				method: "GET",
				url: "/base64/" + longValue,
			});

			expect(response.statusCode).toBe(200);
			expect(response.payload).toBe(
				"The quick brown fox jumps over the lazy dog. This sentence contains every letter of the English alphabet and is often used for typing practice, font testing, and encoding experiments. Base64 encoding transforms this readable text into a sequence of ASCII characters that can safely be transmitted or stored in systems that handle text-only data."
			);
		} finally {
			await mock.close();
		}
	});

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it has been resolved by pushing commit f1970ea

@cyfung1031

cyfung1031 commented Jul 17, 2026

Copy link
Copy Markdown
Author

Sorry for using Vibe Coding.

In one of my projects (https://github.com/scriptscat/scriptcat/),
I am considering to migrate from https://httpbun.com/ to https://mockhttp.org/
However, I found that the url length cannot be too long.

Screenshot 2026-07-18 at 7 33 51

Hope you might consider to merge this PR for bug fixing.

Per review feedback: if an assertion in the test body threw, await
mock.close() below it would never run, leaking the bound port into
later tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@cyfung1031

Copy link
Copy Markdown
Author

Good catch — pushed a fix wrapping the test body in try...finally so mock.close() always runs even if an assertion throws.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants