{"id":"b3d9e9195c65e227","repo":"guzzle/guzzle","slug":"directory-s-does-not-exist-for-sink-value-of-s","errorCode":null,"errorMessage":"Directory %s does not exist for sink value of %s","messagePattern":"Directory (.+?) does not exist for sink value of (.+?)","errorType":"exception","errorClass":"GuzzleHttp\\Exception\\RequestException","httpStatus":null,"severity":"error","filePath":"src/Handler/CurlFactory.php","lineNumber":2519,"sourceCode":"            }\n        }\n\n        $streamFactory = self::requireStreamFactory($options[RequestOptions::STREAM_FACTORY] ?? new HttpFactory());\n        $hasSink = isset($options['sink']);\n        if (!$hasSink) {\n            // Use a default temp stream if no sink was set.\n            $options['sink'] = Psr7\\Utils::tryFopen('php://temp', 'w+');\n        }\n        $sink = $options['sink'];\n        if ($hasSink && \\is_resource($sink)) {\n            $sink = self::streamForResourceSink(Psr7\\Utils::streamFor($sink));\n        } elseif (\\is_resource($sink)) {\n            $sink = $streamFactory->createStreamFromResource($sink);\n        } elseif (!\\is_string($sink)) {\n            $sink = Psr7\\Utils::streamFor($sink);\n        } elseif (!\\is_dir(\\dirname($sink))) {\n            // Ensure that the directory exists before failing in curl.\n            throw new RequestException(\\sprintf('Directory %s does not exist for sink value of %s', Psr7\\DiagnosticValue::escape(\\dirname($sink)), Psr7\\DiagnosticValue::escape($sink)), $easy->request);\n        } else {\n            $sink = new LazyOpenStream($sink, 'w+');\n        }\n        $easy->sink = $sink;\n        $conf[\\CURLOPT_WRITEFUNCTION] = static function ($ch, string $write) use ($easy, $sink): int {\n            $length = \\strlen($write);\n\n            try {\n                $newResponseBodyBytes = TransferByteCounter::add(\n                    $easy->responseBodyBytes,\n                    $length,\n                    'Response body exceeds the maximum integer size supported on this platform'\n                );\n            } catch (\\OverflowException $e) {\n                $easy->responseBodySizeException = $e;\n\n                return 0;\n            }","sourceCodeStart":2501,"sourceCodeEnd":2537,"githubUrl":"https://github.com/guzzle/guzzle/blob/9b200fc5805036b331d6031199880dadecae0275/src/Handler/CurlFactory.php#L2501-L2537","documentation":"Thrown when the 'sink' option is a string file path but the parent directory (dirname($sink)) does not exist. Guzzle validates the directory up front so the failure happens before cURL writes, rather than mid-transfer. Both the missing directory and the sink value are escaped via DiagnosticValue::escape. Use a resource or StreamInterface as the sink if you do not want a filesystem path.","triggerScenarios":"Setting ['sink' => '/tmp/downloads/file.bin'] where /tmp/downloads does not exist; using a configurable output path whose directory was never created; relative sink path expecting a cwd that is not active.","commonSituations":"Download-to-file features where the user-supplied output directory was not mkdir'd; containerized deployments missing a volume mount path; path templating that produces an unexpected directory.","solutions":["Create the destination directory before the request: mkdir(dirname($sink), 0775, true).","Pass an already-open resource or a StreamInterface as 'sink' to bypass filesystem path validation.","Validate/normalize the configured sink path at application startup and create its parent dir."],"exampleFix":"// before\n$client->request('GET', $url, ['sink' => '/var/downloads/report.pdf']);\n// after\n@mkdir('/var/downloads', 0775, true);\n$client->request('GET', $url, ['sink' => '/var/downloads/report.pdf']);","handlingStrategy":"validation","validationCode":"if (isset($options['sink']) && is_string($options['sink']) && !is_dir(dirname($options['sink']))) {\n    @mkdir(dirname($options['sink']), 0775, true);\n}","typeGuard":"function sinkDirectoryExists($sink): bool {\n    return !is_string($sink) || is_dir(dirname($sink));\n}","tryCatchPattern":null,"preventionTips":["mkdir the parent directory (recursively) before sending the download request.","Pass a resource or StreamInterface sink to avoid filesystem path checks.","Validate configured sink paths at app bootstrap."],"tags":["download","sink","filesystem","configuration"],"analyzedSha":"9b200fc5805036b331d6031199880dadecae0275","analyzedAt":"2026-08-04T21:24:26.648Z","schemaVersion":2}