Index: cpp/frams/util/extvalue.cpp
===================================================================
--- cpp/frams/util/extvalue.cpp	(revision 482)
+++ cpp/frams/util/extvalue.cpp	(revision 490)
@@ -357,4 +357,11 @@
 }
 
+void ExtValue::setError(const SString& msg)
+{
+ErrorObject *err=new ErrorObject;
+err->message=msg;
+setObject(ErrorObject::makeDynamicObject(err));
+}
+
 static ExtValue::CompareResult longsign(paInt x)
 {
@@ -369,4 +376,11 @@
 	if ((v.getType() == TInt) && (v.getInt() == 0)) return ExtValue::ResultUnequal_RelaxedEqual;
 	return ExtValue::ResultUnequal_RelaxedUnequal; //comparing anything else with null is valid but null is neither higher nor lower than numbers or strings
+}
+
+static ExtValue::CompareResult compareInvalid(const ExtValue& v)
+{
+	if (v.getType()==TInvalid) return ExtValue::ResultEqualUnordered;
+	if ((v.getType() == TInt) && (v.getInt() == 0)) return ExtValue::ResultUnequal_RelaxedEqual;
+	return ExtValue::ResultMismatch; //comparing anything else with invalid is invalid
 }
 
@@ -392,4 +406,8 @@
 	else if (src.isNull())
 		return compareNull(*this);
+	if (getType()==TInvalid)
+		return compareInvalid(src);
+	else if (src.getType()==TInvalid)
+		return compareInvalid(*this);
 	switch (type)
 	{
@@ -1121,17 +1139,22 @@
 		return in + 5;
 	}
-	else if (!strncmp(in, "invalid", 9))
+	else if (!strncmp(in, "invalid", 7))
 	{
 		setInvalid();
-		return in + 9;
+		return in + 7;
 	}
 	else if (*in == '<')
 	{ //unserializable object
-		setInvalid();
-		while (*in)
-			if (*in == '>')
-				return in + 1;
-			else in++;
-			return in;
+		const char* end=in+1;
+		while (*end)
+			if (*end == '>')
+				{
+				setError(SString("Unserializable class: ")+SString(in+1,end-in-1));
+				return end+1;
+				}
+			else
+				end++;
+		logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing '>'");
+		return NULL;
 	}
 	else if (*in == '^')
@@ -1236,2 +1259,42 @@
 	return v.toString();
 }
+
+#define FIELDSTRUCT ErrorObject
+static ParamEntry errorobject_paramtab[]=
+{
+{"Error",1,3,"Error",},
+{"message",0,0,"Message","s",FIELD(message),},
+{"newFromString",0,0,"create new object","p oError(s message)",PROCEDURE(p_newfromstring),},
+{"toString",0,PARAM_READONLY | PARAM_NOSTATIC,"Textual form","s",GETONLY(toString),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+
+Param& ErrorObject::getParam()
+{
+	static Param param(errorobject_paramtab);
+	return param;
+}
+
+ExtObject ErrorObject::makeDynamicObject(ErrorObject* e)
+{
+	return ExtObject(&getParam(), (DestrBase*)e);
+}
+
+const SString ErrorObject::TO_STRING_PREFIX="Error: ";
+
+void ErrorObject::get_toString(ExtValue* ret)
+{
+ret->setString(TO_STRING_PREFIX+message);
+}
+
+void ErrorObject::p_newfromstring(ExtValue *args, ExtValue *ret)
+{
+ErrorObject *err=new ErrorObject();
+err->message=args[0].getString();
+if (err->message.startsWith(TO_STRING_PREFIX.c_str()))
+	err->message=err->message.substr(TO_STRING_PREFIX.len());
+*ret = makeDynamicObject(err);
+}
+
+REGISTER_DESERIALIZABLE(ErrorObject)
Index: cpp/frams/util/extvalue.h
===================================================================
--- cpp/frams/util/extvalue.h	(revision 482)
+++ cpp/frams/util/extvalue.h	(revision 490)
@@ -185,4 +185,5 @@
 	void setEmpty();
 	void setInvalid() { setEmpty(); type = TInvalid; }
+	void setError(const SString& msg);
 	bool makeUnique() { return (type == TObj) && odata().makeUnique(); } //< @return false if nothing has changed
 	ExtPType getType() const { return type; }
@@ -243,3 +244,17 @@
 #define REGISTER_DESERIALIZABLE(name) ExtValue::AddDeserializable<name> deserializable_autoinit_ ## name;
 
-#endif
+class ErrorObject: public DestrBase
+{
+  public:
+SString message;
+static Param& getParam();
+static Param& getStaticParam() {return getParam();}
+static ExtObject makeDynamicObject(ErrorObject* e);
+static const SString TO_STRING_PREFIX;
+#define STATRICKCLASS ErrorObject
+PARAMGETDEF(toString);
+PARAMPROCDEF(p_newfromstring);
+#undef STATRICKCLASS
+};
+
+#endif
