diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,7 @@
+### 0.26.17
+
++ More robust support for inout arguments, motivated by [issue 472](https://github.com/haskell-gi/haskell-gi/issues/472).
+
 ### 0.26.15
 
 + Fix a code generation mistake when tranferring GClosure arguments of
diff --git a/haskell-gi.cabal b/haskell-gi.cabal
--- a/haskell-gi.cabal
+++ b/haskell-gi.cabal
@@ -1,5 +1,5 @@
 name:                haskell-gi
-version:             0.26.16
+version:             0.26.17
 synopsis:            Generate Haskell bindings for GObject Introspection capable libraries
 description:         Generate Haskell bindings for GObject Introspection capable libraries. This includes most notably
                      GTK, but many other libraries in the GObject ecosystem provide introspection data too.
@@ -12,7 +12,7 @@
 stability:           Experimental
 category:            Development
 build-type:          Custom
-tested-with:         GHC == 8.4.1, GHC == 8.6.1, GHC == 8.8.1, GHC == 8.10.1, GHC == 9.0.1, GHC == 9.2.1
+tested-with:         GHC == 8.4.1, GHC == 8.6.1, GHC == 8.8.1, GHC == 8.10.1, GHC == 9.0.1, GHC == 9.2.1, GHC == 9.4, GHC == 9.6, GHC == 9.8, GHC == 9.10, GHC == 9.12
 cabal-version:       2.0
 
 extra-source-files: ChangeLog.md
diff --git a/lib/Data/GI/CodeGen/Callable.hs b/lib/Data/GI/CodeGen/Callable.hs
--- a/lib/Data/GI/CodeGen/Callable.hs
+++ b/lib/Data/GI/CodeGen/Callable.hs
@@ -390,33 +390,26 @@
 prepareInoutArg arg = do
   name' <- prepareInArg arg
   ft <- foreignType $ argType arg
-  allocInfo <- typeAllocInfo (argType arg)
 
-  case allocInfo of
-    Just (TypeAlloc allocator n) -> do
-      when (transfer arg == TransferNothing && not (argCallerAllocates arg)) $
-        -- Although meaningful, this is almost always a bug in the
-        -- introspection data. Better not generating code than
-        -- generating code that crashes.
-        notImplementedError "‘transfer-ownership=none’ ‘caller-allocates=0’ inout structs not supported"
-
-      wrapMaybe arg >>= bool
-        (do
-            name'' <- genConversion (prime name') $
-                      literal $ M $ allocator <>
-                          " :: " <> typeShow (io ft)
-            line $ "memcpy " <> name'' <> " " <> name' <> " " <> tshow n
-            return name'')
-        -- The semantics of this case are somewhat undefined.
-        (notImplementedError "Nullable inout structs not supported")
-    Nothing -> do
-      if argCallerAllocates arg
-      then return name'
-      else do
-        name'' <- genConversion (prime name') $
-                  literal $ M $ "allocMem :: " <> typeShow (io $ ptr ft)
-        line $ "poke " <> name'' <> " " <> name'
-        return name''
+  if argCallerAllocates arg
+    then return name'
+    else do
+    -- Check that if the argument type is managed, the C type is a
+    -- pointer to a pointer. This is not perfect, but it helps to
+    -- detect some relatively common introspection errors that would
+    -- lead to crashes.
+    managed <- isManaged (argType arg)
+    when managed $
+      case argCType arg of
+        Nothing -> badIntroError $ "Missing C type for argument ‘" <>
+                   argCName arg <> "’"
+        Just ctype -> when (not $ "**" `T.isSuffixOf` ctype) $
+                      badIntroError $ "C type for argument ‘" <>
+                      argCName arg <> "’ is not a pointer to a pointer"
+    name'' <- genConversion (prime name') $
+              literal $ M $ "allocMem :: " <> typeShow (io $ ptr ft)
+    line $ "poke " <> name'' <> " " <> name'
+    return name''
 
 prepareOutArg :: Arg -> ExcCodeGen Text
 prepareOutArg arg = do
diff --git a/lib/Data/GI/GIR/Arg.hs b/lib/Data/GI/GIR/Arg.hs
--- a/lib/Data/GI/GIR/Arg.hs
+++ b/lib/Data/GI/GIR/Arg.hs
@@ -14,7 +14,7 @@
 
 import Data.GI.GIR.BasicTypes (Transfer(..), Type)
 import Data.GI.GIR.Parser
-import Data.GI.GIR.Type (parseType)
+import Data.GI.GIR.Type (parseType, queryElementCType)
 
 data Direction = DirectionIn
                | DirectionOut
@@ -33,6 +33,7 @@
                            -- escaped name valid in Haskell code, use
                            -- `GI.SymbolNaming.escapedArgName`.
         argType :: Type,
+        argCType :: Maybe Text,
         direction :: Direction,
         mayBeNull :: Bool,
         argDoc :: Documentation,
@@ -89,9 +90,11 @@
   -- We will use some heuristics later for setting this field.
   let callbackUserData = False
   t <- parseType
+  maybeCType <- queryElementCType
   doc <- parseDocumentation
   return $ Arg { argCName = name
                , argType = t
+               , argCType = maybeCType
                , argDoc = doc
                , direction = d
                , mayBeNull = mayBeNull
