sipeed/picoclaw · error

deltachat: invalid chatmail server marker %q; use settings.e

Error message

deltachat: invalid chatmail server marker %q; use settings.email like %q

What it means

The `@server` marker contains characters that can never be a domain: a second `@`, or any slash, backslash, space, tab, CR, or LF. parseDeltaChatEmailSetting rejects these to prevent injecting malformed values into the DCACCOUNT QR URL built as `DCACCOUNT:https://<domain>/new` and into shell/terminal output.

Source

Thrown at pkg/channels/deltachat/deltachat.go:178

func parseDeltaChatEmailSetting(value string) (string, bool, error) {
	email := strings.TrimSpace(value)
	if email == "" {
		return "", false, fmt.Errorf(
			"deltachat: email is required.\nNext step: choose one of the chatmail servers below and set channel_list.deltachat.settings.email to %q, or use the same @server form with another chatmail relay. Run `picoclaw g` again; PicoClaw will create the account, print the generated full email address, and stop so you can save that address in the config.\nAvailable chatmail servers:\n%s",
			"@"+defaultChatmailRelays[0].Domain,
			formatChatmailRelayList(),
		)
	}
	if !strings.HasPrefix(email, "@") {
		return email, false, nil
	}
	domain := strings.ToLower(strings.TrimSpace(strings.TrimPrefix(email, "@")))
	if domain == "" {
		return "", false, fmt.Errorf("deltachat: email %q is missing a chatmail server. Use %q or one of:\n%s",
			email, "@"+defaultChatmailRelays[0].Domain, formatChatmailRelayList())
	}
	if strings.Contains(domain, "@") || strings.ContainsAny(domain, "/\\ \t\r\n") {
		return "", false, fmt.Errorf("deltachat: invalid chatmail server marker %q; use settings.email like %q",
			email, "@"+defaultChatmailRelays[0].Domain)
	}
	return domain, true, nil
}

func formatChatmailRelayList() string {
	var b strings.Builder
	for _, relay := range defaultChatmailRelays {
		fmt.Fprintf(&b, "  @%-34s %s\n", relay.Domain, relay.Location)
	}
	return strings.TrimRight(b.String(), "\n")
}

func buildChatmailAccountQR(domain string) string {
	return fmt.Sprintf("DCACCOUNT:https://%s/new", domain)
}

// NewDeltaChatChannel validates config and resolves the RPC server + data dir.

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Set email to exactly `@domain.tld` with no spaces, slashes, or extra @ — e.g. `@nine.testrun.org`.
  2. If you meant a full address, drop the leading @ and use `user@server` directly (that form is accepted and returns early).
  3. Check the YAML quoting: use a plain double-quoted single-line scalar for email.
  4. If templating, strip whitespace/newlines from the substituted server variable.

Example fix

# before
settings:
  email: "@nine.testrun.org extra"   # or "@user@nine.testrun.org"

# after
settings:
  email: "@nine.testrun.org"
Defensive patterns

Strategy: validation

Validate before calling

func validChatmailMarker(v string) bool {
    if !strings.HasPrefix(v, "@") { return true }
    d := strings.ToLower(strings.TrimSpace(strings.TrimPrefix(v, "@")))
    return d != "" && !strings.Contains(d, "@") && !strings.ContainsAny(d, "/\\ \t\r\n")
}

Type guard

func validChatmailMarker(v string) bool {
    if !strings.HasPrefix(v, "@") { return true }
    d := strings.TrimPrefix(v, "@")
    return !strings.ContainsAny(d, "@/\\ \t\r\n")
}

Try / catch

if err != nil && strings.Contains(err.Error(), "invalid chatmail server marker") {
    return errors.New("email @marker must be exactly one domain: @nine.testrun.org")
}

Prevention

When it happens

Trigger: settings.email values like `@a@b`, `@server path`, `@server/path`, `@server\path`, or a multi-line YAML block scalar leaking a newline into the value.

Common situations: YAML folded scalars or quoted strings containing spaces; paste of a full email into the @-marker slot (user@host becomes @user@host after edits); config values copied from prose that includes a trailing comment or space.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/1e7c5bbcd5f2e752. Report an issue: GitHub.