nexu-io/open-design · warning

Enter a valid http(s) website URL.

Error message

Enter a valid http(s) website URL.

What it means

Thrown by startBrandExtraction when the user supplied a url but normalizeUrl(rawUrl) returned null. normalizeUrl prepends https:// when no scheme is present, parses with new URL, and only accepts http/https protocols. The route handler maps this message to HTTP 400 (bad request).

Source

Thrown at apps/daemon/src/brands/index.ts:289

  try {
    return new URL(url).hostname.replace(/^www\./i, '');
  } catch {
    return url;
  }
}

/**
 * Reserve a brand and stand up the agent-driven extraction project. Throws on
 * an invalid URL (the route maps that to a 400). The caller navigates into the
 * returned project and auto-sends the seeded prompt to start the agent.
 */
export async function startBrandExtraction(
  opts: StartBrandExtractionOptions,
): Promise<StartBrandExtractionResult> {
  const designMd = normalizeDesignMdInput(opts.designMd);
  const rawUrl = (opts.url ?? '').trim();
  const url = rawUrl ? normalizeUrl(rawUrl) : designMd ? sourceUrlForDesignMd(designMd, opts.description) : null;
  if (rawUrl && !url) throw new Error('Enter a valid http(s) website URL.');
  if (!url) throw new Error('Enter a valid http(s) website URL or paste a DESIGN.md.');
  const hasWebsiteSource = /^https?:\/\//i.test(url);
  const hasDesignMdSource = Boolean(designMd);

  const {
    brandsRoot,
    projectsRoot,
    skillsRoot,
    db,
    randomId = randomUUID,
    logoFallback = ensureLogoFallback,
    seedFallback = ensureBrandSeed,
    imageryFallback = ensureImageryFallback,
  } = opts;
  const id = newBrandId(url);
  const projectId = brandProjectId(id);
  const conversationId = randomId();
  const host = hostnameOf(url);

View on GitHub (pinned to 5be4028344)

Solutions

  1. Trim whitespace and ensure the URL has an http or https scheme (or no scheme, which normalizeUrl will prefix).
  2. Reject non-http(s) schemes early on the client.
  3. Validate with new URL(value) and check protocol is http: or https: before submitting.

Example fix

// before
await startBrandExtraction({ url: 'ftp://example.com', /* deps */ }); // throws

// after
await startBrandExtraction({ url: 'https://example.com', /* deps */ });
Defensive patterns

Strategy: validation

Validate before calling

function normalizeUrl(raw: string): string | null {
  const trimmed = (raw ?? '').trim();
  if (!trimmed) return null;
  const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
  let parsed: URL;
  try { parsed = new URL(withScheme); } catch { return null; }
  if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return null;
  return parsed.href;
}
const normalized = normalizeUrl(userInput);
if (userInput.trim() && !normalized) {
  throw new Error('Enter a valid http(s) website URL.');
}

Type guard

function isHttpUrl(raw: string): boolean {
  try {
    const u = new URL(/^https?:\/\//i.test(raw) ? raw : `https://${raw}`);
    return u.protocol === 'http:' || u.protocol === 'https:';
  } catch {
    return false;
  }
}

Try / catch

try {
  await startBrandExtraction({ url: userInput, /* deps */ });
} catch (err) {
  if (err instanceof Error && err.message === 'Enter a valid http(s) website URL.') {
    return badRequest('Please enter a valid http or https URL.');
  }
  throw err;
}

Prevention

When it happens

Trigger: The URL is malformed ('htp://example', 'example..com'), uses an unsupported scheme ('ftp://', 'file://', 'mailto:'), contains characters that make new URL throw, or is structurally unparseable.

Common situations: User pastes with a typo in the scheme, trailing whitespace, missing dot, or surrounding quotes; CSV/bulk import containing malformed rows; client not trimming input.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/f9e9b84991fe88d3. Report an issue: GitHub.