You put a comma before every entry, so the final text starts with a comma; it's a prefix on each entry, not just a separator.  I would make it a suffix on each entry conditional on if (rp->next), so that it's actually a separator.
Then again, if you don't need to RlistDestroy(promise_meta), I'd go so far as to rewrite the whole as
```
const Rlist *rp = PromiseGetConstraintAsList(ctx, "meta, pp);
while (rp)
{
    BufferAppendString(tag_buffer, RlistScalarValue(rp));
    rp = rp->next;
    if (rp) { BufferAppendChar(tag_buffer, ','); }
}
```