SEMS fails to parse YETI configuration — unexpected token '}' at line 193

Hello YETI-Switch Community,

I am installing YETI-Switch on a new server and I am currently blocked by a SEMS configuration parsing error. My installation environment is Debian GNU/Linux 13 (trixie) amd64. The installed YETI/SEMS packages are: sems 1.201.2, libsems1 1.201.2, sems-modules-base 1.201.2, sems-modules-yeti 1.14.47core201, yeti-web 1.14.79-1, and postgresql-18-yeti 1.14.1. The SEMS binary is /usr/bin/sems and the systemd service uses ExecStart=/usr/bin/sems -f /etc/sems/sems.conf.

The package information was obtained with:

$ dpkg -l | grep -Ei ‘sems|yeti’

ii libsems1 1.201.2 amd64 SIP Express Media Server, shared library
ii postgresql-18-yeti 1.14.1 amd64 provides YETI extension for PostgreSQL
ii sems 1.201.2 amd64 SIP Express Media Server, very fast and flexible SIP media server
ii sems-modules-base 1.201.2 amd64 SIP Express Media Server, base applications, plugins and codecs
ii sems-modules-yeti 1.14.47core201 amd64 YETI SBC application module for SEMS
ii yeti-web 1.14.79-1 amd64 UI for YETI project

When I run SEMS directly in debug mode with:

$ sems -E -D3 -f /etc/sems/sems.conf

I receive:

1787561161.830797 [#2b5f7080/496754] [cfg_error_callback, AmLcConfig.cpp:662] ERROR: /etc/sems/sems.conf:193 [module/yeti]: unexpected token ‘}’
1787561161.830831 [#2b5f7080/496754] [readConfiguration, AmLcConfig.cpp:994] ERROR: failed to parse configuration file: /etc/sems/sems.conf
1787561161.830836 [#2b5f7080/496754] [main, sems.cpp:496] ERROR: configuration errors. exiting.

The systemd service is currently failed with exit code 255:

$ systemctl status sems --no-pager

sems.service - SEMS for YETI project
Loaded: loaded (/usr/lib/systemd/system/sems.service; enabled; preset: enabled)
Active: failed (Result: exit-code)
Main PID: 14424 (code=exited, status=255/EXCEPTION)

The relevant section of /etc/sems/sems.conf is:

routing {
    schema = switch22
    function = route_release
    init = init
    pass_input_interface_name = true
    new_codec_groups = true

    headers {
        header(x-yeti-auth)
        header(diversion, json, uri_json_array)
        header(x-orig-ip, inet)
        header(x-orig-port, integer)
        header(x-orig-proto, smallint)
        header(p-asserted-identity, json, uri_json_array)
        header(p-preferred-identity, json, uri_json)
        header(privacy)
        header(remote-party-id, json, uri_json_array)
        header(rpid-privacy)
    }

    master_pool {
        host = 127.0.0.1
        port = 5432
        name = yeti
        user = yeti
        pass = \[REDACTED\]
        size = 4
    }

    failover_to_slave = false

    slave_pool {
        host = 127.0.0.1
        port = 5432
        name = yeti
        user = yeti
        pass = \[REDACTED\]
        size = 4
    }
}

The parser specifically reports the closing brace of master_pool at line 193:

186 master_pool {
187 host = 127.0.0.1
188 port = 5432
189 name = yeti
190 user = yeti
191 pass = [REDACTED]
192 size = 4
193 }

I have checked the configuration file for formatting problems. It has no CRLF/DOS line endings, it is ASCII text, and no suspicious non-printable characters were detected. The SEMS binary executes correctly and the YETI module package is installed.

I also searched the installed /usr/share, /usr/lib and /opt/yeti-web directories for references to master_pool, slave_pool and failover_to_slave, but did not find a usable configuration template.

Could you please confirm: (1) whether the master_pool/slave_pool syntax above is correct for sems-modules-yeti 1.14.47core201; (2) whether sems 1.201.2 is compatible with this YETI module version; (3) whether there is a specific YETI sems.conf template/configuration for this exact package combination; (4) whether size = 4 or another parameter is causing the parser to reject the master_pool block; and (5) if possible, provide the correct routing database-pool configuration for this exact YETI version.

I would prefer to correct the configuration rather than reinstall or randomly modify the YETI installation. I can provide the complete sanitized /etc/sems/sems.conf, additional package information, and further debug output if required.

Thank you for your help.

provide full configuration file, including all sections.

Sorry, how you expect us to use it? Could you format your message properly?

Hi, as requested, I have provided the complete /etc/sems/sems.conf configuration below. I have redacted the PostgreSQL passwords for security. I am posting the configuration in a single code block because the forum editor was not preserving the formatting correctly when I pasted the file normally.

The exact error when starting SEMS is:

ERROR: /etc/sems/sems.conf:193 [module/yeti]: unexpected token ‘}’

System/package versions:

SEMS: 1.201.2
sems-modules-yeti: 1.14.47core201
yeti-web: 1.14.79-1
postgresql-18-yeti: 1.14.1
OS: Debian 13 amd64

The complete configuration is:

general {
    stderr = no
    syslog_loglevel = 2
    syslog_facility = LOCAL0

    node_id = 1337

    shutdown_mode {
        code = 508
        reason = "Yeti node in shutdown mode"
        allow_uac = true
    }

    media_processor_threads = 4
    rtp_receiver_threads = 4
    session_processor_threads = 20
    sip_udp_server_threads = 2
    sip_tcp_server_threads = 2

    dead_rtp_time = 30

    default_bl_ttl = 0

    symmetric_rtp_mode = packets
    symmetric_rtp_packets = 20
}

signaling-interfaces {
    interface primary {
        default-media-interface = primary
        ip4 {
            sip-udp {
                address = 2.25.78.51
                port = 5060
                use-raw-sockets = off
            }
            sip-tcp {
                address = 2.25.78.51
                port = 5060
                connect-timeout = 2000
                static-client-port = on
                idle-timeout=900000
                use-raw-sockets = off
            }
        }
    }
}

media-interfaces {
    interface primary {
        ip4 {
            rtp {
                address = 2.25.78.51
                low-port = 16384
                high-port = 32769
                dscp = 46
                use-raw-sockets = off
            }
        }
    }
}

modules {
    module "di_log" {}

    module "mp3" {}
    module "opus" {}
    module "wav" {}
    module "gsm" {}
    module "ilbc" {}
    module "adpcm" {}
    module "l16" {}
    module "g722" {}

    module "registrar_client" {}
    module "postgresql" {}

    module "session_timer" {}
    module "jsonrpc" {
        listen{
            address = 127.0.0.1
            port = 7080
        }
        server_threads = 1
        map(yeti.request.registrations.reload, yeti.request.db_states.reload)
        map(yeti.request.router.translations.reload, yeti.request.db_states.reload)
        map(yeti.request.router.codec-groups.reload, yeti.request.db_states.reload)
        map(yeti.request.sensors.reload, yeti.request.db_states.reload)
        map(yeti.request.radius.authorization.profiles.reload, yeti.request.db_states.reload)
        map(yeti.request.radius.accounting.profiles.reload, yeti.request.db_states.reload)
        map(yeti.request.auth.credentials.reload, yeti.request.db_states.reload)
        map(yeti.request.options_prober.reload, yeti.request.db_states.reload)
    }

    module "http_client" {}

    module-global "uac_auth" {}
    module "options_prober" {}

    module "radius_client" {}

    module "redis" {
        max_batch_size = 10
        batch_timeout = 100
        max_queue_size = 1000
    }

    module "registrar" {
        redis {
            write {
                hosts = { 127.0.0.1:6379 }
            }
            read {
                hosts = { 127.0.0.1:6379 }
            }
       }

        headers = { x-orig-ip, x-orig-port, x-orig-proto }
    }

    module "yeti" {
        pop_id = 1337
        lega_cdr_headers {
            add_q850_reason = yes

            header(x-yeti-auth, string)
            header(x-orig-ip, string)
            header(x-orig-port, string)
            header(x-orig-proto, string)

            header(p-charge-info, string, p_charge_info_in, String)
            header(diversion, array)
            header(p-asserted-identity, array)
            header(p-preferred-identity, string)
            header(privacy, array)
            header(remote-party-id, array)
            header(rpid-privacy, array)
        }

        legb_cdr_headers {
            header(diversion, array)
            header(p-asserted-identity, array)
            header(p-preferred-identity, string)
            header(privacy, array)
            header(remote-party-id, array)
            header(rpid-privacy, array)
        }

        legb_response_cdr_headers {
            add_q850_reason = yes
        }

        write_internal_disconnect_code = yes

        auth {
            realm = yeti-switch
        }

        msg_logger_dir = /var/spool/sems/dump
        audio_recorder_dir = /var/spool/sems/records
        audio_recorder_compress = false

        db_refresh_interval = 5
        ip_auth_reject_if_no_matched = true

        routing {
            schema = switch22
            function = route_release
            init = init
            pass_input_interface_name = true
            new_codec_groups = true

            headers {
                header(x-yeti-auth)
                header(diversion, json, uri_json_array)
                header(x-orig-ip, inet)
                header(x-orig-port, integer)
                header(x-orig-proto, smallint)
                header(p-asserted-identity, json, uri_json_array)
                header(p-preferred-identity, json, uri_json)
                header(privacy)
                header(remote-party-id, json, uri_json_array)
                header(rpid-privacy)
            }

            master_pool {
                host = 127.0.0.1
                port = 5432
                name = yeti
                user = yeti
                pass = [REDACTED]
                size = 4
            }

            failover_to_slave = false
            slave_pool {
                host = 127.0.0.1
                port = 5432
                name = yeti
                user = yeti
                pass = [REDACTED]
                size = 4
            }
        }

        cdr {
            pool_size = 2
            auth_pool_size = 2
            schema = switch
            function = writecdr
            master {
                host = 127.0.0.1
                port = 5432
                name = cdr
                user = cdr
                pass = [REDACTED]
            }
            failover_to_slave = false
            slave {
                host = 127.0.0.1
                port = 5432
                name = cdr
                user = cdr
                pass = [REDACTED]
            }
            batch_size = 1
            batch_timeout = 5000
            auth_batch_size = 1
            auth_batch_timeout = 20000
        }

        resources {
            reject_on_error = false
            write {
                hosts = { 127.0.0.1:6379 }
                timeout = 500
            }
            read {
                hosts = { 127.0.0.1:6379 }
                timeout = 1000
            }
        }

        rpc {
            calls_show_limit = 10000
        }
    }
}

routing {
    application = yeti
}

Could you please review the configuration and confirm whether the syntax is correct for SEMS 1.201.2 / YETI SEMS module 1.14.47core201? In particular, I would appreciate confirmation whether the master_pool / slave_pool configuration under module "yeti" -> routing is correct for this version, or whether this may be related to a documentation or module issue.

Thank you.

It looks correct, so problem may be caused by symbols in your passwords. What symbols you have there?

Hi, thank you. The PostgreSQL passwords contain special characters. The YETI database password contains /, +, and = characters, and the CDR database password also contains /, +, and = characters. I have redacted the actual passwords from my community post for security. If these characters can cause a parsing issue in SEMS 1.201.2, please let me know the correct syntax/escaping method for the password, or whether I should temporarily change the PostgreSQL passwords to a simpler password containing only letters and numbers for testing. I can perform that test and report the result.