serve-instrument exits when an instrument directory is deleted or a request URL has a malformed percent-escape
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 82/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- node.js, typescript
Research direction
Start with packages/serve-instrument/src/server.tsx at the watcher callback, updateEncodedBundle, request handler, and URL decoding points named in the issue. Then read packages/serve-instrument/src/__tests__/server.test.ts and run the focused tests. Done when the deletion and malformed-URL cases return the expected responses without stopping the server or disrupting other instruments.
Written by the indexing model from the issue text.
Description
serve-instrument exits with an unhandled error when an instrument directory is deleted or renamed while it runs, or when it receives a URL with a malformed percent-escape. In both cases an exception escapes an async function that is called with void: this.handler.handle(...) for requests, and this.updateEncodedBundle() in the fs.watch callback. Node turns the unhandled rejection into a process exit. In updateEncodedBundle, only the bundle() call is inside the try; the readdir/readFile before it are not. In --all mode, deleting or renaming one instrument folder kills the server for all the others, and so does a git checkout that removes one. An author has to notice that the terminal died and restart it.
Where
packages/serve-instrument/src/server.tsx:45-48, 62-79 and 243-247, 312:
this.watcher = fs.watch(target, { recursive: false }, () => {
// ...
void this.updateEncodedBundle();
});
// ...
private async updateEncodedBundle(): Promise<void> {
const inputs: BundlerInput[] = [];
const filepaths = await fs.promises
.readdir(this.target, 'utf-8')
// ...
for (const filepath of filepaths) {
const loader = inferLoader(filepath);
const content: string | Uint8Array = await fs.promises.readFile(filepath, /* ... */);
// ...
}
const start = Date.now();
try {
// ...
const decodedName = decodeURIComponent(name!);
// ...
this.server = http.createServer((...args) => void this.handler.handle(...args));
Reproduce
- Build it (
pnpm --filter @opendatacapture/serve-instrument build) and runnode dist/cli.js --all <dir>on a directory containingforms/FOO/index.tsandforms/BAR/index.ts. - Open
/forms/BAR, thenrm -rf <dir>/forms/BAR.
Actual:↺ [forms/BAR] File changed, rebuilding..., thenError: ENOENT: no such file or directory, scandir '…/forms/BAR'and the process exits (verified onmain)./forms/FOOis no longer served. - Restart, then
curl 'http://localhost:3000/forms/%E0%A4%A'.
Actual:URIError: URI malformedatdecodeURIComponentand the process exits (verified onmain).
Expected: the server logs the failure and keeps running. A deleted instrument answers 404 and a malformed URL answers 400.
Tests
packages/serve-instrument/src/__tests__/server.test.ts:
it('should keep serving other instruments after an instrument directory is deleted, so a rebuild failure does not kill the server'): fire the captured watch callback after removing the temp directory, and assert that the remaining instrument still returns 200.it('should answer 400 to a malformed percent-escape in an instrument URL rather than crashing')
No e2e test applies: serve-instrument is not part of the stack under testing/.
Suggested fix
Move the directory listing and file reads inside the existing try in updateEncodedBundle. Wrap handle in Server's request callback with a .catch that logs the error and ends the response with 500 if headers are not yet sent. Map a URIError from decodeURIComponent to 400.
- Dominant language
- TypeScript
- Stars
- 119
- Forks
- 19
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 44
Getting set up
- Ships a Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from DouglasNeuroInformatics/OpenDataCapture
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
DouglasNeuroInformatics/OpenDataCapture#1813 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
DouglasNeuroInformatics/OpenDataCapture#1812 ·
Maintainers usually reply within 1 day
-
Area: Playground Bug Difficulty: Low Good First Issue Priority: Low
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
DouglasNeuroInformatics/OpenDataCapture#1805 ·
Maintainers usually reply within 1 day
-
Area: Instruments Bug Difficulty: Low Priority: Low
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
DouglasNeuroInformatics/OpenDataCapture#1801 ·
Maintainers usually reply within 1 day
-
Area: Instruments Bug Difficulty: Low Good First Issue Priority: Low
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
DouglasNeuroInformatics/OpenDataCapture#1800 ·
Maintainers usually reply within 1 day
All issues in DouglasNeuroInformatics/OpenDataCapture
Similar issues
-
bnv-manager-v2: accept_invite creates the Mailcow domain admin before the commitPossibly taken @myOmikron claimed this today. Openbug ready-for-agent
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
platform:windows tool triaged wontfix
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
nicolettas-muggelbude/RechnungsFee#448 ·
Maintainers usually reply within 1 day
-
Android bug feature/error-tracking react-native team/error-tracking
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
PostHog/posthog-js#5310 · 1 comment ·
Maintainers usually reply within 1 day