{"record":{"id":"cf83d1f995339e52","repo":"microsoft/playwright","slug":"cannot-write-to-a-directory","errorCode":null,"errorMessage":"Cannot write to a directory","messagePattern":"Cannot write to a directory","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/dispatchers/writableStreamDispatcher.ts","lineNumber":46,"sourceCode":"  readonly lastModifiedMs: number | undefined;\n\n  constructor(parent: SdkObject, streamOrDirectory: fs.WriteStream | string, lastModifiedMs: number | undefined) {\n    super(parent, 'stream');\n    this.streamOrDirectory = streamOrDirectory;\n    this.lastModifiedMs = lastModifiedMs;\n  }\n}\n\nexport class WritableStreamDispatcher extends Dispatcher<WritableStreamSdkObject, channels.WritableStreamChannel, BrowserContextDispatcher> implements channels.WritableStreamChannel {\n  _type_WritableStream = true;\n\n  constructor(scope: BrowserContextDispatcher, streamOrDirectory: fs.WriteStream | string, lastModifiedMs?: number) {\n    super(scope, new WritableStreamSdkObject(scope._object, streamOrDirectory, lastModifiedMs), 'WritableStream', {});\n  }\n\n  async write(params: channels.WritableStreamWriteParams, progress: Progress): Promise<channels.WritableStreamWriteResult> {\n    if (typeof this._object.streamOrDirectory === 'string')\n      throw new Error('Cannot write to a directory');\n    const stream = this._object.streamOrDirectory;\n    await progress.race(new Promise<void>((fulfill, reject) => {\n      stream.write(params.binary, error => {\n        if (error)\n          reject(error);\n        else\n          fulfill();\n      });\n    }));\n  }\n\n  async close(params: channels.WritableStreamCloseParams, progress: Progress): Promise<void> {\n    if (typeof this._object.streamOrDirectory === 'string')\n      throw new Error('Cannot close a directory');\n    const stream = this._object.streamOrDirectory;\n    await progress.race(new Promise<void>(fulfill => stream.end(fulfill)));\n    if (this._object.lastModifiedMs)\n      await progress.race(fs.promises.utimes(this.path(), new Date(this._object.lastModifiedMs), new Date(this._object.lastModifiedMs)));","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/dispatchers/writableStreamDispatcher.ts#L28-L64","documentation":"WritableStreamDispatcher can wrap either an fs.WriteStream (a real writable file) or a plain string representing a directory path (used internally when the target is a directory marker). The write() method throws when streamOrDirectory is a string, because bytes cannot be appended to a directory. This guard catches misuse of the protocol-level stream API.","triggerScenarios":"Constructing a WritableStreamDispatcher with a directory string (as saveAs does to mark a directory upload target) and then calling write() on it; passing a directory path where a file write stream was expected through the saveAs / saveStorageState path.","commonSituations":"This is primarily an internal/protocol-level error surfaced when a save target resolves to a directory rather than a file — e.g. a download or HAR saveAs path that points at an existing directory; rarely hit directly by end-user code.","solutions":["Pass a concrete file path, not a directory, when creating a writable stream / saveAs target.","Construct the destination with path.join(dir, 'file.ext') so it always resolves to a file.","Verify the path with fs.statSync(path).isFile() before writing."],"exampleFix":"// before\nawait download.saveAs('/tmp/downloads/'); // directory → write throws\n// after\nawait download.saveAs('/tmp/downloads/report.pdf'); // file path","handlingStrategy":"validation","validationCode":"// Ensure the save target is a file path, not a directory.\nconst dest = path.join(downloadDir, suggestedName);\nif (fs.statSync(dest).isDirectory()) throw new Error('target is a directory');\nawait download.saveAs(dest);","typeGuard":"// Confirm the path resolves to a file before writing.\nfunction isFilePath(p: string): boolean {\n  try { return fs.statSync(p).isFile(); } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Always pass a full file path (path.join(dir, name)) to saveAs, never a bare directory.","Verify the destination with fs.statSync(...).isFile() before writing.","Treat directory-string stream wrappers as read-only markers."],"tags":["filesystem","save-as","protocol"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}