First note that your definition of sow can be simplified to:
var sow = Object(Symbol());
which does the same thing as your code, when in sloppy mode.
When you call String with an argument of value, per the specification:
a. If NewTarget is undefined and Type(value) is Symbol, return SymbolDescriptiveString(value).
b. Let s be ? ToString(value).
Here, since you've wrapped the symbol in an object, its type is not a symbol; rather, it's an object, so ToString is called. This, when called with an object, will first coerce the object to a primitive, then call ToString on it:
- Let primValue be ? ToPrimitive(argument, hint String).
- Return ? ToString(primValue).
But symbols cannot have ToString called on them; as you can see in the same table there, when passed a Symbol, ToString will throw a TypeError.
That's why String(sow); works. In contrast, when you do sow.toString(), you're calling Symbol.prototype.toString with a calling context (a this) of the object-wrapped symbol:
var sow = Object(Symbol());
console.log(sow.toString === Symbol.prototype.toString);
Which does something somewhat different. Symbol.prototype.toString retrieves the underlying Symbol from the object, and then calls SymbolDescriptiveString with the Symbol, resulting in a string with the symbol's descriptive value (if any).
Note that a native Symbol not wrapped in an object does not throw when passed to String because the String constructor will not call the problematic ToString abstract method when the argument is a symbol.
const sym = Symbol();
console.log(String(sym));
console.log(sym.toString());