jmoiron/sqlx · error

unexpected `:` while reading named param at

Error message

unexpected `:` while reading named param at 

What it means

When compiling a named query, sqlx treats ':' as the start of a named parameter (:name). A second ':' encountered while still inside a parameter name is only legal as the PostgreSQL cast escape '::'. Any other unexpected ':' inside a name (e.g. '::' logic confusion or ':a:b') means the named syntax is malformed, so compilation fails with this error including the byte offset.

Source

Thrown at named.go:349

func compileNamedQuery(qs []byte, bindType int) (query string, names []string, err error) {
	names = make([]string, 0, 10)
	rebound := make([]byte, 0, len(qs))

	inName := false
	last := len(qs) - 1
	currentVar := 1
	name := make([]byte, 0, 10)

	for i, b := range qs {
		// a ':' while we're in a name is an error
		if b == ':' {
			// if this is the second ':' in a '::' escape sequence, append a ':'
			if inName && i > 0 && qs[i-1] == ':' {
				rebound = append(rebound, ':')
				inName = false
				continue
			} else if inName {
				err = errors.New("unexpected `:` while reading named param at " + strconv.Itoa(i))
				return query, names, err
			}
			inName = true
			name = []byte{}
		} else if inName && i > 0 && b == '=' && len(name) == 0 {
			rebound = append(rebound, ':', '=')
			inName = false
			continue
			// if we're in a name, and this is an allowed character, continue
		} else if inName && (unicode.IsOneOf(allowedBindRunes, rune(b)) || b == '_' || b == '.') && i != last {
			// append the byte to the name if we are in a name and not on the last byte
			name = append(name, b)
			// if we're in a name and it's not an allowed character, the name is done
		} else if inName {
			inName = false
			// if this is the final byte of the string and it is part of the name, then
			// make sure to add it to the name
			if i == last && unicode.IsOneOf(allowedBindRunes, rune(b)) {

View on GitHub (pinned to 41dac167fd)

Solutions

  1. Fix the query so each ':' begins a valid name followed by alphanumerics and ends cleanly
  2. Escape a literal '::' cast properly (sqlx handles '::' — ensure there is no stray single ':' before it)
  3. Rename params so none contain ':' inside the name (e.g. :from_date not :from:date)
  4. Use '?' positional binding with sqlx.In if the query needs complex placeholder syntax

Example fix

// before
q := "SELECT * FROM t WHERE created::date = :day AND ts:::tz IS NULL"
// after
q := "SELECT * FROM t WHERE created::date = :day AND ts::timestamptz IS NULL"
Defensive patterns

Strategy: validation

Validate before calling

func validateNamedParams(query string) error {
    for i := 0; i < len(query); i++ {
        if query[i] == ':' {
            if i+1 < len(query) && query[i+1] == ':' { i++; continue } // :: cast
            j := i + 1
            for j < len(query) && (query[j] == '_' || isAlnum(query[j])) { j++ }
            if j == i+1 { return fmt.Errorf("empty named param at %d", i) }
            if j < len(query) && query[j] == ':' { return fmt.Errorf("unexpected ':' after param at %d", j) }
            i = j - 1
        }
    }
    return nil
}

Try / catch

query, args, err := db.PrepareNamed(q)
if err != nil {
    if strings.Contains(err.Error(), "unexpected `:`") {
        log.Printf("malformed named param near offset in %s", q)
    }
    return err
}

Prevention

When it happens

Trigger: Calling NamedQuery/PrepareNamed/Get/Select with a query containing malformed named params like ":a:b", or a lone ':' immediately after an incomplete name, where compileNamedQuery's inName state meets another ':' that isn't part of '::'.

Common situations: Mixing PostgreSQL casts (::type) with named params and mis-escaping; accidentally writing two adjacent named params without text between them; porting queries from other libraries with different placeholder syntaxes.

Related errors


AI-assisted analysis of jmoiron/sqlx@41dac167fd (2026-09-03). Data as JSON: /api/errors/34aa2818a1523d1f. Report an issue: GitHub.