{"record":{"id":"8ebb5a3ece3f85b6","repo":"transloadit/uppy","slug":"no-destination-specified","errorCode":null,"errorMessage":"no destination specified","messagePattern":"no destination specified","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"packages/@uppy/companion/src/server/Uploader.ts","lineNumber":153,"sourceCode":"  // validate headers (optional)\n  if (options.headers != null && typeof options.headers !== 'object') {\n    throw new ValidationError('headers must be an object')\n  }\n\n  // validate protocol (optional)\n  if (\n    options.protocol &&\n    !Object.values(PROTOCOLS).includes(options.protocol)\n  ) {\n    throw new ValidationError('unsupported protocol specified')\n  }\n\n  // s3 uploads don't require upload destination\n  // validation, because the destination is determined\n  // by the server's s3 config\n  if (options.protocol !== PROTOCOLS.s3Multipart) {\n    if (!options.endpoint && !options.uploadUrl) {\n      throw new ValidationError('no destination specified')\n    }\n\n    const validateUrl = (url: string | undefined): void => {\n      if (url == null) return\n      const validatorOpts = { require_protocol: true, require_tld: false }\n      if (!validator.isURL(url, validatorOpts)) {\n        throw new ValidationError('invalid destination url')\n      }\n\n      const allowedUrls = options.companionOptions.uploadUrls\n      if (allowedUrls && !hasMatch(url, allowedUrls)) {\n        throw new ValidationError(\n          'upload destination does not match any allowed destinations',\n        )\n      }\n    }\n\n    ;[options.endpoint, options.uploadUrl].forEach(validateUrl)","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/companion/src/server/Uploader.ts#L135-L171","documentation":"Thrown by Companion's Uploader.validateOptions when a non-s3Multipart upload is created without any destination. Uploader needs somewhere to POST/PUT the file: either `options.endpoint` or `options.uploadUrl` must be set. s3Multipart uploads are exempt because the destination is derived from the server's S3 configuration.","triggerScenarios":"POSTing to /uppy/upload with protocol 'multipart' or 'tus' but omitting both `endpoint` and `uploadUrl` fields in the request body; or constructing `new Uploader({...})` server-side without either property (and not using PROTOCOLS.s3Multipart).","commonSituations":"Client-side misconfiguration where the Uppy companion-client is not given a companionUrl/endpoint, a typo like `endpont`, or assuming S3 mode while the server resolved protocol to multipart/tus.","solutions":["Set `endpoint` (or `uploadUrl`) in the upload request / Uploader options, e.g. endpoint: 'https://example.com/upload'","If you intend S3, pass protocol: 's3Multipart' AND configure S3 keys on the Companion server so the s3 branch is valid","Verify the client's companionOptions and that companion-client forwards the destination field"],"exampleFix":"// before\nawait companionClient.upload({ protocol: 'multipart' }) // no destination\n\n// after\nawait companionClient.upload({ protocol: 'multipart', endpoint: 'https://my-server.example.com/receive' })","handlingStrategy":"validation","validationCode":"const opts = { protocol: 'multipart', endpoint: undefined, uploadUrl: undefined }\nif (opts.protocol !== 's3Multipart' && !opts.endpoint && !opts.uploadUrl) {\n  throw new Error('client-side precheck: no destination specified')\n}","typeGuard":"const hasUploadDestination = (o: { endpoint?: unknown; uploadUrl?: unknown; protocol?: string }) =>\n  o.protocol === 's3Multipart' || typeof o.endpoint === 'string' || typeof o.uploadUrl === 'string';","tryCatchPattern":"try { await uploader.upload() } catch (err) {\n  if (err instanceof ValidationError && err.message === 'no destination specified') { /* prompt user/config for destination */ }\n}","preventionTips":["Always set endpoint or uploadUrl for multipart/tus uploads","Default the endpoint in one config module so every call site inherits it"],"tags":["companion","uploader","validation","destination"],"backgroundTag":"missing-required-option","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}