diff --git a/src/lib/project-saver-hoc.jsx b/src/lib/project-saver-hoc.jsx
index 2dbc3374712b88c4ed5f4a924d30353a48b08d7a..dc3ee65cbc0e60092dda9d1e882d9e18d2a3dd7b 100644
--- a/src/lib/project-saver-hoc.jsx
+++ b/src/lib/project-saver-hoc.jsx
@@ -234,9 +234,14 @@ const ProjectSaverHOC = function (WrappedComponent) {
                         asset.dataFormat,
                         asset.data,
                         asset.assetId
-                    ).then(
-                        () => (asset.clean = true)
-                    )
+                    ).then(response => {
+                        // Asset servers respond with {status: ok} for successful POSTs
+                        if (response.status !== 'ok') {
+                            // Errors include a `code` property, e.g. "Forbidden"
+                            return Promise.reject(response.code);
+                        }
+                        asset.clean = true;
+                    })
                 )
             )
                 .then(() => this.props.onUpdateProjectData(projectId, savedVMState, requestParams))
diff --git a/src/lib/save-project-to-server.js b/src/lib/save-project-to-server.js
index 12301bbbd44e0e0d01f1da059d6cd82a23e2402b..2584923e60d8f1f27250aed4edf2e9157f885f1f 100644
--- a/src/lib/save-project-to-server.js
+++ b/src/lib/save-project-to-server.js
@@ -45,6 +45,7 @@ export default function (projectId, vmState, params) {
     return new Promise((resolve, reject) => {
         xhr(opts, (err, response) => {
             if (err) return reject(err);
+            if (response.statusCode !== 200) return reject(response.statusCode);
             let body;
             try {
                 // Since we didn't set json: true, we have to parse manually