toeverything/AFFiNE · error · GraphqlBadRequest

caldav_invalid_url

caldav_invalid_url

Error message

CalDAV URL is invalid.

What it means

The CalDAV provider's assertAllowedUrl (caldav.ts) parses the user-supplied URL with new URL(); a parse failure throws GraphqlBadRequest with code caldav_invalid_url. This is the first gate before the protocol, host-allowlist, and SSRF checks run.

Source

Thrown at packages/backend/server/src/plugins/calendar/providers/caldav.ts:563

        maxBytes: DEFAULT_MAX_RESPONSE_BYTES,
        allowedHeaders: CALDAV_SAFE_FETCH_HEADERS,
        allowedHosts: this.allowedHosts,
        allowHttp: this.allowInsecureHttp,
        allowPrivateTargetOrigin: !this.blockPrivateNetwork,
      });
    } catch (error) {
      const ssrfError = this.toGraphqlSsrfError(error);
      if (ssrfError) throw ssrfError;
      throw error;
    }
  }

  private async assertAllowedUrl(urlValue: string) {
    let url: URL;
    try {
      url = new URL(urlValue);
    } catch {
      throw new GraphqlBadRequest({
        code: 'caldav_invalid_url',
        message: 'CalDAV URL is invalid.',
      });
    }

    if (
      url.protocol !== 'https:' &&
      !(url.protocol === 'http:' && this.allowInsecureHttp)
    ) {
      throw new GraphqlBadRequest({
        code: 'caldav_insecure_url',
        message: 'CalDAV URL must use https.',
      });
    }

    const hostname = url.hostname.toLowerCase();
    if (
      this.allowedHosts.length &&

View on GitHub (pinned to b4c8548c09)

Solutions

  1. Enter a full absolute URL including the scheme, e.g. https://caldav.example.com/user/.
  2. Trim whitespace and strip stray characters before submitting.
  3. Validate with new URL() on the client before calling the API.

Example fix

// before
await caldav.connect('caldav.example.com/user'); // no scheme -> caldav_invalid_url

// after
const url = 'caldav.example.com/user'.trim();
const normalized = /^https?:\/\//.test(url) ? url : `https://${url}`;
await caldav.connect(normalized);
Defensive patterns

Strategy: validation

Validate before calling

function isValidHttpUrl(v: string): boolean {
  try { new URL(v.trim()); return true; } catch { return false; }
}
if (!isValidHttpUrl(caldavUrl)) {
  // show 'Enter a full URL, e.g. https://...' before calling the API
}

Type guard

function isParsableUrl(v: string): v is `${'http' | 'https'}://${string}` {
  try { new URL(v); return true; } catch { return false; }
}

Try / catch

try { await caldav.connect(url); } catch (e) { if (e.code === 'caldav_invalid_url') showUrlFormatError(); else throw e; }

Prevention

When it happens

Trigger: Saving or linking a CalDAV calendar subscription with a URL that cannot be parsed — missing scheme (example.com/dav), stray whitespace or punctuation, or a bare hostname.

Common situations: User omits https:// when entering the CalDAV endpoint; copy-paste includes trailing punctuation or invisible whitespace; client sends just a hostname or a malformed user:pass@host construction.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/ed40f28d4a53ccd6. Report an issue: GitHub.