outline/outline · error · Error
Must be a valid url
Error message
Must be a valid url
What it means
The IsUrl decorator validates that a value is a well-formed URL with http/https protocol required, and (in cloud-hosted mode) a TLD required. A value missing the protocol, using a non-http scheme, or lacking a TLD in cloud mode throws at save time.
Source
Thrown at server/models/validators/IsUrl.ts:21
import env from "@server/env";
/**
* A decorator that validates that a string is a valid HTTP(S) url. A top-level
* domain is only required when cloud hosted, allowing self-hosted installations
* to use internal hostnames.
*/
export default function IsUrl(target: object, propertyName: string) {
return addAttributeOptions(target, propertyName, {
validate: {
validUrl(value: string) {
if (
!isURL(value, {
protocols: ["http", "https"],
require_protocol: true,
require_tld: env.isCloudHosted,
})
) {
throw new Error("Must be a valid url");
}
},
},
});
}
View on GitHub (pinned to 935a44d4c0)
Solutions
- Prefix the value with https:// (or http:// for local) before saving.
- In self-hosted deployments where localhost is valid, ensure isCloudHosted is false.
- Sanitize/normalize URL input client-side using the URL constructor.
Example fix
// before user.avatarUrl = 'example.com/avatar.png'; // after user.avatarUrl = 'https://example.com/avatar.png';
Defensive patterns
Strategy: validation
Validate before calling
let normalized = value.trim();
if (!/^https?:\/\//i.test(normalized)) normalized = `https://${normalized}`;
try { new URL(normalized); } catch { throw new ValidationError('Must be a valid url'); } Type guard
const isHttpUrl = (v: string): v is string => {
try { const u = new URL(v); return u.protocol === 'http:' || u.protocol === 'https:'; } catch { return false; }
}; Prevention
- Always require the protocol in user-entered URLs.
- On self-hosted, confirm isCloudHosted is false if localhost URLs are valid.
- Normalize via the URL constructor before persisting.
When it happens
Trigger: Saving a URL column (e.g. avatar URL, integration webhook) with a value like 'example.com/path' (no protocol), 'ftp://x', or 'http://localhost' while isCloudHosted is true (no TLD).
Common situations: Users pasting URLs without https://; self-hosted dev values pointing at localhost while the cloud-hosted flag is on; webhook URLs with custom schemes.
Related errors
- Must be a URL or relative path
- Must be a fully qualified domain name
- Must be a valid hex color code
- msg
- Invalid data
AI-assisted analysis of outline/outline@935a44d4c0 (2026-08-12).
Data as JSON: /api/errors/a91fdf0598bda748.
Report an issue: GitHub.