decolua/9router · error · Error

Invalid callback URL format

Error message

Invalid callback URL format

What it means

handleManualSubmit in KiroSocialOAuthModal parses the manually pasted callback URL with `new URL(callbackUrl)`; when the string is not a valid absolute URL the constructor throws and this wrapper converts it into a readable Error. The modal accepts either `kiro://...` or `http://localhost...` callback shapes, both of which must still be parseable by the URL constructor (kiro:// works because it has scheme + rest).

Source

Thrown at src/shared/components/KiroSocialOAuthModal.js:70

        setError(err.message);
        setStep("error");
      }
    };

    initAuth();
  }, [isOpen, provider]);

  const handleManualSubmit = async () => {
    try {
      setError(null);
      
      // Parse callback URL - can be either kiro:// or http://localhost format
      let url;
      try {
        url = new URL(callbackUrl);
      } catch (e) {
        // If URL parsing fails, might be malformed
        throw new Error("Invalid callback URL format");
      }

      const code = url.searchParams.get("code");
      const state = url.searchParams.get("state");
      const errorParam = url.searchParams.get("error");

      if (errorParam) {
        throw new Error(url.searchParams.get("error_description") || errorParam);
      }

      if (!code) {
        throw new Error("No authorization code found in URL");
      }

      // Exchange code for tokens
      const res = await fetch("/api/oauth/kiro/social-exchange", {
        method: "POST",
        headers: { "Content-Type": "application/json" },

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Re-copy the entire callback URL, including the scheme (kiro:// or http://), and paste it again.
  2. Trim whitespace: paste after `.trim()`-ing the input, or check the raw string in devtools.
  3. If your deep link doesn't include a scheme, prepend one manually (e.g. kiro://callback?code=...&state=...).
  4. Verify the URL contains `code` and `state` query params — if you only got the code, this error may appear first for malformed input.

Example fix

// before
let url;
try {
  url = new URL(callbackUrl);
} catch (e) {
  throw new Error("Invalid callback URL format");
}
// after
let url;
try {
  url = new URL(callbackUrl.trim());
} catch (e) {
  throw new Error(`Invalid callback URL format: "${callbackUrl.slice(0, 80)}" (expected kiro://... or http://localhost/...?code=...)`);
}
Defensive patterns

Strategy: validation

Validate before calling

function isValidCallbackUrl(s) {
  if (typeof s !== "string") return false;
  try {
    const u = new URL(s.trim());
    return u.protocol === "kiro:" || (u.protocol === "http:" || u.protocol === "https:");
  } catch {
    return false;
  }
}
// call before handleManualSubmit
if (!isValidCallbackUrl(callbackUrl)) {
  setError("Paste the full callback URL (kiro://... or http://localhost/...?code=...)");
  return;
}

Type guard

function asParsedUrl(value) {
  try { const u = new URL(String(value).trim()); return /^kiro:|^https?:$/.test(u.protocol) ? u : null; }
  catch { return null; }
}

Try / catch

try {
  const url = asParsedUrl(callbackUrl);
  if (!url) throw new Error(`Invalid callback URL format: "${String(callbackUrl).slice(0, 80)}"`);
  // proceed
} catch (err) {
  setError(err.message);
}

Prevention

When it happens

Trigger: User pastes something into the manual callback input that `new URL()` cannot parse: an empty string, a bare authorization code, a URL missing its scheme (e.g. "localhost:PORT/callback?code=..." is actually misparsed), truncated copy/paste, or text with stray whitespace/newlines.

Common situations: Copying only part of the callback URL from the browser address bar; pasting the code alone instead of the full URL; the kiro:// deep link got mangled by the terminal or chat client; leading/trailing whitespace from copying.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/b02559e249429272. Report an issue: GitHub.