Using l3regex directly instead of \regexpatchcmd it works:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \addinnertoouter { m }
{
\regex_replace_once:nnNTF
{ \c{outercmd} \{ (.*) \} \Z}
{ \c{outercmd} \cB\{ \1 \c{inner} \cB\{ #1 \cE\} \cE\} }
\test
{ }
{ error }
}
\ExplSyntaxOff
\begin{document}
\ttfamily
\def\test{\outercmd{\inner{abc}}}
\meaning\test
\addinnertoouter{def}
\meaning\test
\addinnertoouter{ghi}
\meaning\test
\end{document}

(\outer is a (very special) TeX primitive, so it's better if you use something else.)
The issue is that l3regex apparently makes the added def tokens catcode 12 (looks like a bug to me; I'll summon a wizard to check), and when \regexpatchcmd tries to retokenize them they become catcode 11, and \regexpatchcmd thinks (correctly) that the macro cannot be patched. If you don't mind that the inserted tokens be catcode 12 (in this case it probably won't matter), then \regex_replace_once:nnNTF will suit your needs.
Beware that the argument of your \addinnertoouter is still a regular expression, so if you do \addinnertoouter{\textit{jkl}} the output will not be what you expect. You can use the \u feature of l3regex to add an arbitrary token list (without catcode changes, so it will also work with \regexpatchcmd):
\documentclass{article}
\usepackage{regexpatch}
\ExplSyntaxOn
\tl_new:N \l_bers_tmpa_tl
\NewDocumentCommand \addinnertoouter { m }
{
\tl_set:Nn \l_bers_tmpa_tl {#1}
\regexpatchcmd
\test
{ \c{outercmd} \{ (.*) \} \Z}
{ \c{outercmd} \cB\{ \1 \c{inner} \cB\{ \u{l_bers_tmpa_tl} \cE\} \cE\} }
{ }
{ error }
}
\ExplSyntaxOff
\begin{document}
\ttfamily
\def\test{\outercmd{\inner{abc}}}
\meaning\test
\addinnertoouter{def}
\meaning\test
\addinnertoouter{ghi}
\meaning\test
\addinnertoouter{\textit{jkl}}
\meaning\test
\end{document}

\tl_build_...described ininterface3? – egreg Sep 02 '19 at 10:42tl_build_endcommand will be difficult. (I might be able to do it before using the command.) – bers Sep 02 '19 at 10:51