instructure/canvas-lms · error · GraphQL::CoercionError
# is not a valid StringMap
Error message
#{input_value.inspect} is not a valid StringMap What it means
StringMapType is a scalar whose coerce_input validates that the value is a Hash of String keys to String values (ActionController::Parameters are first converted to a plain Hash). Anything else — nested hashes, non-string values, arrays, nil — raises GraphQL::CoercionError with this message naming the offending value.
Solutions
- Stringify every key and value before sending: Object.entries(obj) mapped to String(v).
- Reject or coerce null/undefined/numeric/boolean values client-side; omit keys whose values are null.
- If you need structured (non-string) data, use a different argument type — StringMap is flat strings only.
- Flatten nested objects to string values (e.g. JSON.stringify the sub-object as the value).
Example fix
// before
variables: { metadata: { attempts: 3, notes: null } }
// after
variables: { metadata: { attempts: "3", notes: "" } } // or omit null keys Defensive patterns
Strategy: validation
Validate before calling
function isStringMap(v) {
if (v == null || typeof v !== 'object' || Array.isArray(v)) return false;
return Object.entries(v).every(([k, val]) => typeof k === 'string' && typeof val === 'string');
}
if (!isStringMap(vars.metadata)) throw new Error('metadata must be a flat String->String map'); Type guard
const isStringMap = (v) => v !== null && typeof v === 'object' && !Array.isArray(v) && Object.values(v).every(x => typeof x === 'string') && Object.keys(v).every(k => typeof k === 'string');
Try / catch
try { await gql(MUTATION, vars); } catch (e) { if (e.message.includes('is not a valid StringMap')) { vars = stringifyMapShallow(vars.metadata); return retry(vars); } throw e; } Prevention
- Coerce all values with String(v) and drop null/undefined entries before sending.
- Never nest objects in a StringMap — JSON.stringify sub-objects as string values if needed.
- Add a schema-aware variables validator (e.g. GraphQL Code Generator / validation against the introspected scalar) in your client.
When it happens
Trigger: Passing a variable with non-string values (numbers, booleans, nulls) or nested objects into a StringMap-typed argument; sending an array instead of an object; sending a multipart/GraphQL-upload parameter that remains an unconvertible type.
Common situations: JS clients stringify JSON in variables but forget to coerce numeric fields (e.g. {"count": 5}); building the map from parsed form data that kept integer values; locale-specific metadata fields where one value is null.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- filter values must be JSON-compatible types
- A maximum of 50 assessees can be provided at once
- A maximum of 50 assessors can be provided at once
- All ConversationMessages must exist within the same…
- Anonymous assignments must be manually posted
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/98c009c62f68b5d2.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/types/string_map_type.rb:33
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
module Types
class StringMapType < Types::BaseScalar
description "A hash with string keys and string values"
def self.coerce_input(input_value, _context)
return nil if input_value.nil?
if input_value.is_a?(ActionController::Parameters)
input_value = input_value.to_unsafe_h
end
unless input_value.is_a?(Hash) && input_value.all? { |k, v| k.is_a?(String) && v.is_a?(String) }
raise GraphQL::CoercionError, "#{input_value.inspect} is not a valid StringMap"
end
input_value
end
def self.coerce_result(ruby_value, _context)
unless ruby_value.is_a?(Hash) && ruby_value.all? { |k, v| (k.is_a?(Symbol) || k.is_a?(String)) && v.is_a?(String) }
raise GraphQL::CoercionError, "#{ruby_value.inspect} is not a valid StringMap"
end
ruby_value.transform_keys(&:to_s)
end
end
end
View on GitHub (pinned to 1c9f0bb801)