{"record":{"id":"a2740a07a72394cb","repo":"mikefarah/yq","slug":"cannot-encode-v-as-uri-can-only-operate-on-strin","errorCode":null,"errorMessage":"cannot encode %v as URI, can only operate on strings. Please first pipe through another encoding operator to convert the value to a string","messagePattern":"cannot encode (.+?) as URI, can only operate on strings\\. Please first pipe through another encoding operator to convert the value to a string","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/yqlib/encoder_sh.go","lineNumber":36,"sourceCode":"func NewShEncoder() Encoder {\n\treturn &shEncoder{false}\n}\n\nfunc (e *shEncoder) CanHandleAliases() bool {\n\treturn false\n}\n\nfunc (e *shEncoder) PrintDocumentSeparator(_ io.Writer) error {\n\treturn nil\n}\n\nfunc (e *shEncoder) PrintLeadingContent(_ io.Writer, _ string) error {\n\treturn nil\n}\n\nfunc (e *shEncoder) Encode(writer io.Writer, node *CandidateNode) error {\n\tif node.guessTagFromCustomType() != \"!!str\" {\n\t\treturn fmt.Errorf(\"cannot encode %v as URI, can only operate on strings. Please first pipe through another encoding operator to convert the value to a string\", node.Tag)\n\t}\n\n\treturn writeString(writer, e.encode(node.Value))\n}\n\n// put any (shell-unsafe) characters into a single-quoted block, close the block lazily\nfunc (e *shEncoder) encode(input string) string {\n\tconst quote = '\\''\n\tvar inQuoteBlock = false\n\tvar encoded strings.Builder\n\tencoded.Grow(len(input))\n\n\tfor _, ir := range input {\n\t\t// open or close a single-quote block\n\t\tif ir == quote {\n\t\t\tif inQuoteBlock {\n\t\t\t\t// get out of a quote block for an input quote\n\t\t\t\tencoded.WriteRune(quote)","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/mikefarah/yq/blob/8b5af0694bb82b41d4ae180fac9972029066f90a/pkg/yqlib/encoder_sh.go#L18-L54","documentation":"The sh (shell quoting) encoder can only serialize string nodes. It checks `node.guessTagFromCustomType() != \"!!str\"` and rejects anything else (ints, bools, maps, seqs) telling you to convert the value to a string first. The message mentions URI because the sh encoder is shared with the URI output path.","triggerScenarios":"`yq -o sh '.count' file.yaml` where `.count` is `!!int`; piping a boolean/null node to `-o sh`; encoding a map/sequence node with `-o sh`/`@uri`.","commonSituations":"Exporting numeric env vars for `eval` in shell scripts; quoting timestamps or booleans for shell; forgetting YAML auto-typing turns `true`, `123`, `null` into non-string tags.","solutions":["Force string conversion: `yq -o sh '.count | tostring' file.yaml`.","Or retag: `yq -o sh '.count tag=\"!!str\"' file.yaml`.","For multiple values, map over them: `yq -o sh '.[] | tostring'`.","Quote in the YAML source (`count: \"123\"`) if the value is inherently textual."],"exampleFix":"# before\nyq -o sh '.port' config.yaml      # port: 8080 (!!int) -> error\n\n# after\nyq -o sh '.port | tostring' config.yaml","handlingStrategy":"type-guard","validationCode":"[ \"$(yq '.port | tag' config.yaml)\" = \"!!str\" ] || echo \"pipe through tostring\"","typeGuard":"isStringNode() { [ \"$(yq \"$1\" | yq 'tag')\" = \"!!str\" ]; }","tryCatchPattern":"val=$(yq -o sh '.port | tostring' config.yaml) || val=$(yq -o sh '.port' config.yaml)","preventionTips":["Always append `| tostring` when exporting values to -o sh","Quote inherently-textual values in the YAML source","Remember YAML auto-typing: 123, true, null are not strings"],"tags":["shell","encoder","string","type-mismatch"],"backgroundTag":"encoder-requires-string-node","analyzedSha":"8b5af0694bb82b41d4ae180fac9972029066f90a","analyzedAt":"2026-09-05T10:57:22.766Z","contentChangedAt":"2026-09-05T10:57:22.766Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}