Summary
Uploading an attachment with a non-ASCII filename (e.g. Chinese characters like 截图.png) causes the server process to crash when the attachment is later downloaded. The Content-Disposition response header is set with raw non-ASCII bytes, which Node.js v24 strictly rejects with ERR_INVALID_CHAR, resulting in an unhandled exception that terminates the process.
Surface Area
- Server:
packages/server/src/api/attachments.ts — encodeRfc6266Filename() + download route
Environment
- version: 0.5.20
- install method: Docker image (
harbor.dns.guazi.com/first-tree/first-tree:0.5.20)
- operating system: Linux (private deployment)
- Node.js: v24.19.0
Reproduction
- Deploy First Tree 0.5.20 via Docker
- Upload any attachment whose filename contains non-ASCII characters (e.g.
截图.png)
- Trigger an attachment download:
GET /api/v1/attachments/<uuid>
- Server process crashes immediately
Expected Behavior
The attachment downloads successfully. The Content-Disposition header uses RFC 6266 filename*=UTF-8''... encoding (percent-encoded) for non-ASCII filenames, or at minimum escapes all non-ASCII bytes so the header value is safe for Node.js HTTP.
Actual Behavior
The server crashes with an unhandled TypeError:
node:_http_outgoing:690
throw new ERR_INVALID_CHAR('header content', name);
TypeError [ERR_INVALID_CHAR]: Invalid character in header content ["content-disposition"]
at ServerResponse.setHeader (node:_http_outgoing:690:11)
at sendStream (/app/node_modules/.pnpm/fastify@5.8.2/node_modules/fastify/lib/reply.js:815:11)
...
Root Cause
encodeRfc6266Filename() in packages/server/src/api/attachments.ts only encodes CR, LF, ", and \:
function encodeRfc6266Filename(name: string): string {
return name.replace(/[\r\n"\\]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`);
}
Non-ASCII characters (e.g. Chinese, Japanese, emoji) pass through unencoded. Node.js v24 enforces strict ASCII-only HTTP header values, so the raw non-ASCII bytes in Content-Disposition: inline; filename="截图.png" trigger a fatal throw.
Suggested Fix
Use filename*=UTF-8''<percent-encoded> (RFC 5987 / RFC 6266) for filenames containing non-ASCII characters:
function encodeRfc6266Filename(name: string): string {
// If all ASCII and safe, use simple quoted form
if (/^[\x20-\x7E]*$/.test(name) && !/[\r\n"\\]/.test(name)) {
return `filename="${name}"`;
}
// RFC 5987: filename*=UTF-8''<percent-encoded>
const encoded = encodeURIComponent(name).replace(/['()*]/g, (c) =>
`%${c.charCodeAt(0).toString(16).toUpperCase()}`
);
return `filename*=UTF-8''${encoded}`;
}
Workaround
Rename files to ASCII-only names before uploading (e.g. image.png instead of 截图.png).
Additional Context
Discovered during private deployment testing. The crash is 100% reproducible with any non-ASCII filename. The process exits without a graceful shutdown, causing brief downtime until the container restarts.
Summary
Uploading an attachment with a non-ASCII filename (e.g. Chinese characters like
截图.png) causes the server process to crash when the attachment is later downloaded. TheContent-Dispositionresponse header is set with raw non-ASCII bytes, which Node.js v24 strictly rejects withERR_INVALID_CHAR, resulting in an unhandled exception that terminates the process.Surface Area
packages/server/src/api/attachments.ts—encodeRfc6266Filename()+ download routeEnvironment
harbor.dns.guazi.com/first-tree/first-tree:0.5.20)Reproduction
截图.png)GET /api/v1/attachments/<uuid>Expected Behavior
The attachment downloads successfully. The
Content-Dispositionheader uses RFC 6266filename*=UTF-8''...encoding (percent-encoded) for non-ASCII filenames, or at minimum escapes all non-ASCII bytes so the header value is safe for Node.js HTTP.Actual Behavior
The server crashes with an unhandled
TypeError:Root Cause
encodeRfc6266Filename()inpackages/server/src/api/attachments.tsonly encodesCR,LF,", and\:Non-ASCII characters (e.g. Chinese, Japanese, emoji) pass through unencoded. Node.js v24 enforces strict ASCII-only HTTP header values, so the raw non-ASCII bytes in
Content-Disposition: inline; filename="截图.png"trigger a fatal throw.Suggested Fix
Use
filename*=UTF-8''<percent-encoded>(RFC 5987 / RFC 6266) for filenames containing non-ASCII characters:Workaround
Rename files to ASCII-only names before uploading (e.g.
image.pnginstead of截图.png).Additional Context
Discovered during private deployment testing. The crash is 100% reproducible with any non-ASCII filename. The process exits without a graceful shutdown, causing brief downtime until the container restarts.