diff --git a/music21/duration.py b/music21/duration.py
index 8c6318016..6155728c1 100644
--- a/music21/duration.py
+++ b/music21/duration.py
@@ -2845,7 +2845,10 @@ class Duration(prebase.ProtoM21Object, SlottedObjectMixin):
     @property
     def tuplets(self) -> Tuple[Tuplet, ...]:
         '''
-        return a tuple of Tuplet objects
+        Return a tuple of Tuplet objects.
+        May leave a stream containing objects having this duration
+        in an unusable state, requiring :meth:`~music21.stream.core.coreElementsChanged`
+        to be called. For this reason, prefer using :meth:`appendTuplet` to add tuplets.
         '''
         if self._componentsNeedUpdating:
             self._updateComponents()
diff --git a/music21/stream/base.py b/music21/stream/base.py
index 4d8f92316..c4e39b577 100644
--- a/music21/stream/base.py
+++ b/music21/stream/base.py
@@ -7882,8 +7882,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
             return self._cache['Duration']
         else:
             # environLocal.printDebug(['creating new duration based on highest time'])
-            self._cache['Duration'] = duration.Duration()
-            self._cache['Duration'].quarterLength = self.highestTime
+            self._cache['Duration'] = duration.Duration(quarterLength=self.highestTime)
             return self._cache['Duration']
 
     def _setDuration(self, durationObj):

diff --git a/music21/stream/tests.py b/music21/stream/tests.py
index 679cc8007..3d1165e16 100644
--- a/music21/stream/tests.py
+++ b/music21/stream/tests.py
@@ -545,6 +545,23 @@ class Test(unittest.TestCase):
         self.assertEqual(a.duration.quarterLength, 2.0)
         self.assertEqual(a.highestTime, 4)
 
+    def testStreamDurationRecalculated(self):
+        from fractions import Fraction
+
+        a = Stream()
+        n = note.Note(quarterLength=1.0)
+        a.append(n)
+        self.assertEqual(a.duration.quarterLength, 1.0)
+
+        # Alter the note's duration in a nonstandard way
+        # Normally, one would call Duration.appendTuplet()
+        tup = duration.Tuplet()
+        n.duration.tuplets = (tup,)
+        # Nonstandard workflow requires coreElementsChanged() to be called
+        # https://github.com/cuthbertLab/music21/issues/957
+        a.coreElementsChanged()
+        self.assertEqual(a.duration.quarterLength, Fraction(2, 3))
+
     def testMeasureStream(self):
         '''An approach to setting TimeSignature measures in offsets and durations
         '''

stream._cache can disappear at the most inopportune moment
<!-- Version of music21:  print(music21.__version__) -->
**music21 version**

`6.3.0`   <!-- replace with actual version -->


**Problem summary**
<!-- Briefly: what undesired thing happens when what action is taken? -->
The cache can get cleared between adding _cache_['Duration'] and setting _cache_['Duration'].quarterLength.

**Steps to reproduce**
I set duration.tuplets on a note that was already in a stream. Some time later, someone called stream.duration, and the following code in stream/__init__.py (_getDuration) crashed:

            # environLocal.printDebug(['creating new duration based on highest time'])
            self._cache['Duration'] = duration.Duration()
            self._cache['Duration'].quarterLength = self.highestTime <--- BOOM, KeyError, which seems impossible, right?
            return self._cache['Duration']

Some investigation showed that self.highestTime cleared the cache in coreElementsChanged().  To be more clear, self.highestTime called _getQuarterLength on my modified duration, so updateQuarterLength gets called, which ultimately calls informClient/informSites/coreElementsChanged, which sets stream._cache_ to {}, making:
            self._cache['Duration'].quarterLength = self.highestTime
raise KeyError while trying to evaluate self._cache['Duration'] on an empty cache.

Proposed fix:
    In place of:
            self._cache['Duration'] = duration.Duration()
            self._cache['Duration'].quarterLength = self.highestTime
    do:
            highestTime = self.highestTime # might clear self._cache!
            self._cache['Duration'] = duration.Duration()
            self._cache['Duration'].quarterLength = highestTime

Workaround:
In my new Humdrum converter's tuplet handling code, when I set duration.tuplets on notes that are already in a stream, I now immediately get the quarterLength, to avoid this situation.

```
Paste minimal code example here. Most issues can be reproduced with just a few elements.
Attach external files only after attempting to reproduce using a simple stream.

I'm afraid I don't have a minimal example.

```

**Expected vs. actual behavior**
<!-- Consider annotating the output produced by a function -->

**More information**
<!-- If relevant: OS, suggested fix, attempted workarounds -->

<!-- Note: only Mac and Windows are directly supported by music21 staff.  
     Bug reports that are specific to other OSes w/o a PR will be closed.
 -->

music21/stream/tests.py::Test::testStreamDurationRecalculated
music21/stream/tests.py::Test::testAccumulatedTimeA music21/stream/tests.py::Test::testAccumulatedTimeB music21/stream/tests.py::Test::testActivateVariantsA music21/stream/tests.py::Test::testActivateVariantsB music21/stream/tests.py::Test::testActivateVariantsBySpanA music21/stream/tests.py::Test::testActivateVariantsBySpanB music21/stream/tests.py::Test::testActivateVariantsC music21/stream/tests.py::Test::testActivateVariantsD music21/stream/tests.py::Test::testActivateVariantsE music21/stream/tests.py::Test::testActiveSiteAfterBoolIteration music21/stream/tests.py::Test::testActiveSiteCopiedStreams music21/stream/tests.py::Test::testActiveSiteMangling music21/stream/tests.py::Test::testActiveSites music21/stream/tests.py::Test::testActiveSitesMultiple music21/stream/tests.py::Test::testAddSlurByMelisma music21/stream/tests.py::Test::testAnalyze music21/stream/tests.py::Test::testAppendFails music21/stream/tests.py::Test::testAugmentOrDiminishBasic music21/stream/tests.py::Test::testAugmentOrDiminishCorpus music21/stream/tests.py::Test::testAugmentOrDiminishHighestTimes music21/stream/tests.py::Test::testBestTimeSignature music21/stream/tests.py::Test::testChordifyA music21/stream/tests.py::Test::testChordifyB music21/stream/tests.py::Test::testChordifyC music21/stream/tests.py::Test::testChordifyD music21/stream/tests.py::Test::testChordifyE music21/stream/tests.py::Test::testChordifyF music21/stream/tests.py::Test::testChordifyG music21/stream/tests.py::Test::testChordifyImported music21/stream/tests.py::Test::testChordifyRests music21/stream/tests.py::Test::testChordifyTagPartA music21/stream/tests.py::Test::testChordifyTagPartB music21/stream/tests.py::Test::testClefs music21/stream/tests.py::Test::testContainerHierarchyA music21/stream/tests.py::Test::testContextNestedA music21/stream/tests.py::Test::testContextNestedB music21/stream/tests.py::Test::testContextNestedC music21/stream/tests.py::Test::testContextNestedD music21/stream/tests.py::Test::testDeepcopyActiveSite music21/stream/tests.py::Test::testDeepcopySpanners music21/stream/tests.py::Test::testDerivationA music21/stream/tests.py::Test::testDerivationB music21/stream/tests.py::Test::testDerivationC music21/stream/tests.py::Test::testDerivationMethodA music21/stream/tests.py::Test::testDoubleStreamPlacement music21/stream/tests.py::Test::testElements music21/stream/tests.py::Test::testElementsHighestTimeA music21/stream/tests.py::Test::testElementsHighestTimeB music21/stream/tests.py::Test::testElementsHighestTimeC music21/stream/tests.py::Test::testExtendDurationA music21/stream/tests.py::Test::testExtendTiesA music21/stream/tests.py::Test::testExtendTiesB music21/stream/tests.py::Test::testExtractedNoteAssignLyric music21/stream/tests.py::Test::testFinalBarlinePropertyA music21/stream/tests.py::Test::testFinalBarlinePropertyB music21/stream/tests.py::Test::testFindConsecutiveNotes music21/stream/tests.py::Test::testFindGaps music21/stream/tests.py::Test::testFlatCachingA music21/stream/tests.py::Test::testFlatCachingB music21/stream/tests.py::Test::testFlatCachingC music21/stream/tests.py::Test::testFlatSimple music21/stream/tests.py::Test::testFlattenUnnecessaryVoicesA music21/stream/tests.py::Test::testGetElementAfterElement music21/stream/tests.py::Test::testGetElementAtOrBeforeBarline music21/stream/tests.py::Test::testGetElementBeforeOffsetA music21/stream/tests.py::Test::testGetElementBeforeOffsetB music21/stream/tests.py::Test::testGetElementsByContextStream music21/stream/tests.py::Test::testGetElementsByOffsetZeroLength music21/stream/tests.py::Test::testGetInstrumentFromMxl music21/stream/tests.py::Test::testGetInstrumentManual music21/stream/tests.py::Test::testGetKeySignatures music21/stream/tests.py::Test::testGetKeySignaturesThreeMeasures music21/stream/tests.py::Test::testGetTimeSignatures music21/stream/tests.py::Test::testGetVariantsA music21/stream/tests.py::Test::testGraceChords music21/stream/tests.py::Test::testGracesInStream music21/stream/tests.py::Test::testHaveAccidentalsBeenMadeA music21/stream/tests.py::Test::testHaveAccidentalsBeenMadeB music21/stream/tests.py::Test::testHaveAccidentalsBeenMadeInVoices music21/stream/tests.py::Test::testHaveBeamsBeenMadeA music21/stream/tests.py::Test::testHaveBeamsBeenMadeB music21/stream/tests.py::Test::testInsertAndShiftBasic music21/stream/tests.py::Test::testInsertAndShiftMultipleElements music21/stream/tests.py::Test::testInsertAndShiftNoDuration music21/stream/tests.py::Test::testInsertIntoNoteOrChordA music21/stream/tests.py::Test::testInsertIntoNoteOrChordB music21/stream/tests.py::Test::testInvertDiatonicA music21/stream/tests.py::Test::testInvertDiatonicQuickSearch music21/stream/tests.py::Test::testIsFlat music21/stream/tests.py::Test::testIteration music21/stream/tests.py::Test::testMakeAccidentalsA music21/stream/tests.py::Test::testMakeAccidentalsB music21/stream/tests.py::Test::testMakeAccidentalsC music21/stream/tests.py::Test::testMakeAccidentalsD music21/stream/tests.py::Test::testMakeAccidentalsOctaveKS music21/stream/tests.py::Test::testMakeAccidentalsTies music21/stream/tests.py::Test::testMakeAccidentalsWithKeysInMeasures music21/stream/tests.py::Test::testMakeChordsBuiltA music21/stream/tests.py::Test::testMakeChordsBuiltB music21/stream/tests.py::Test::testMakeChordsBuiltC music21/stream/tests.py::Test::testMakeChordsBuiltD music21/stream/tests.py::Test::testMakeChordsImported music21/stream/tests.py::Test::testMakeMeasuresInPlace music21/stream/tests.py::Test::testMakeMeasuresLastElementNoDuration music21/stream/tests.py::Test::testMakeMeasuresMeterStream music21/stream/tests.py::Test::testMakeMeasuresTimeSignatures music21/stream/tests.py::Test::testMakeMeasuresWithBarlines music21/stream/tests.py::Test::testMakeNotationA music21/stream/tests.py::Test::testMakeNotationB music21/stream/tests.py::Test::testMakeNotationByMeasuresA music21/stream/tests.py::Test::testMakeNotationByMeasuresB music21/stream/tests.py::Test::testMakeNotationC music21/stream/tests.py::Test::testMakeNotationKeySignatureMultiVoice music21/stream/tests.py::Test::testMakeNotationKeySignatureOneVoice music21/stream/tests.py::Test::testMakeNotationScoreA music21/stream/tests.py::Test::testMakeNotationScoreB music21/stream/tests.py::Test::testMakeNotationScoreC music21/stream/tests.py::Test::testMakeRestsA music21/stream/tests.py::Test::testMakeRestsB music21/stream/tests.py::Test::testMakeTies music21/stream/tests.py::Test::testMakeTupletBracketsA music21/stream/tests.py::Test::testMakeTupletBracketsB music21/stream/tests.py::Test::testMakeVoicesA music21/stream/tests.py::Test::testMakeVoicesB music21/stream/tests.py::Test::testMeasureAndTieCreation music21/stream/tests.py::Test::testMeasureBarDurationProportion music21/stream/tests.py::Test::testMeasureBarline music21/stream/tests.py::Test::testMeasureGrouping music21/stream/tests.py::Test::testMeasureLayout music21/stream/tests.py::Test::testMeasureOffsetMap music21/stream/tests.py::Test::testMeasureOffsetMapPostTie music21/stream/tests.py::Test::testMeasureRange music21/stream/tests.py::Test::testMeasureStream music21/stream/tests.py::Test::testMeasuresA music21/stream/tests.py::Test::testMeasuresAndMakeMeasures music21/stream/tests.py::Test::testMeasuresB music21/stream/tests.py::Test::testMeasuresC music21/stream/tests.py::Test::testMeasuresSuffix music21/stream/tests.py::Test::testMelodicIntervals music21/stream/tests.py::Test::testMelodicIntervalsB music21/stream/tests.py::Test::testMergeElements music21/stream/tests.py::Test::testMetadataOnStream music21/stream/tests.py::Test::testMidiEventsBuilt music21/stream/tests.py::Test::testMultipartStream music21/stream/tests.py::Test::testMusicXMLGenerationViaPropertyA music21/stream/tests.py::Test::testMusicXMLGenerationViaPropertyB music21/stream/tests.py::Test::testMusicXMLGenerationViaPropertyC music21/stream/tests.py::Test::testOpusSearch music21/stream/tests.py::Test::testOpusWrite music21/stream/tests.py::Test::testOverlapsA music21/stream/tests.py::Test::testOverlapsB music21/stream/tests.py::Test::testPartDurationA music21/stream/tests.py::Test::testPartDurationB music21/stream/tests.py::Test::testPartsToVoicesA music21/stream/tests.py::Test::testPartsToVoicesB music21/stream/tests.py::Test::testQuantize music21/stream/tests.py::Test::testQuantizeMinimumDuration music21/stream/tests.py::Test::testRecurseA music21/stream/tests.py::Test::testRecurseB music21/stream/tests.py::Test::testRemove music21/stream/tests.py::Test::testRemoveByClass music21/stream/tests.py::Test::testReplace music21/stream/tests.py::Test::testReplaceA1 music21/stream/tests.py::Test::testReplaceB music21/stream/tests.py::Test::testReplaceDerived music21/stream/tests.py::Test::testScaleDurationsBasic music21/stream/tests.py::Test::testScaleOffsetsBasic music21/stream/tests.py::Test::testScaleOffsetsBasicInPlaceA music21/stream/tests.py::Test::testScaleOffsetsBasicInPlaceB music21/stream/tests.py::Test::testScaleOffsetsBasicInPlaceC music21/stream/tests.py::Test::testScaleOffsetsBasicInPlaceD music21/stream/tests.py::Test::testScaleOffsetsNested music21/stream/tests.py::Test::testScoreShowA music21/stream/tests.py::Test::testSecondsMapA music21/stream/tests.py::Test::testSecondsMapB music21/stream/tests.py::Test::testSecondsPropertyA music21/stream/tests.py::Test::testSecondsPropertyB music21/stream/tests.py::Test::testSecondsPropertyC music21/stream/tests.py::Test::testSemiFlatCachingA music21/stream/tests.py::Test::testSetElements music21/stream/tests.py::Test::testSetElementsFromOtherStreamWithEndElements music21/stream/tests.py::Test::testSimpleRecurse music21/stream/tests.py::Test::testSliceAtOffsetsBuilt music21/stream/tests.py::Test::testSliceAtOffsetsImported music21/stream/tests.py::Test::testSliceAtOffsetsSimple music21/stream/tests.py::Test::testSliceByBeatBuilt music21/stream/tests.py::Test::testSliceByBeatImported music21/stream/tests.py::Test::testSliceByGreatestDivisorBuilt music21/stream/tests.py::Test::testSliceByGreatestDivisorImported music21/stream/tests.py::Test::testSliceByQuarterLengthsBuilt music21/stream/tests.py::Test::testSliceByQuarterLengthsImported music21/stream/tests.py::Test::testSort music21/stream/tests.py::Test::testSortAndAutoSort music21/stream/tests.py::Test::testSortingAfterInsertA music21/stream/tests.py::Test::testSpannerTransferA music21/stream/tests.py::Test::testSplitAtQuarterLengthA music21/stream/tests.py::Test::testSplitAtQuarterLengthB music21/stream/tests.py::Test::testSplitAtQuarterLengthC music21/stream/tests.py::Test::testStoreAtEndFailures music21/stream/tests.py::Test::testStreamCopy music21/stream/tests.py::Test::testStreamDuration music21/stream/tests.py::Test::testStreamElementsComparison music21/stream/tests.py::Test::testStreamExceptionsOnAssert music21/stream/tests.py::Test::testStreamRecursion music21/stream/tests.py::Test::testStreamSortRecursion music21/stream/tests.py::Test::testStripTiesBuiltA music21/stream/tests.py::Test::testStripTiesBuiltB music21/stream/tests.py::Test::testStripTiesChordMembersAllTied music21/stream/tests.py::Test::testStripTiesChordMembersSomeTied music21/stream/tests.py::Test::testStripTiesChords music21/stream/tests.py::Test::testStripTiesConsecutiveInVoiceNotContainer music21/stream/tests.py::Test::testStripTiesImportedA music21/stream/tests.py::Test::testStripTiesImportedB music21/stream/tests.py::Test::testStripTiesNonMeasureContainers music21/stream/tests.py::Test::testStripTiesReplaceSpannedElements music21/stream/tests.py::Test::testStripTiesScore music21/stream/tests.py::Test::testTemplateAll music21/stream/tests.py::Test::testTransposeByPitchA music21/stream/tests.py::Test::testTransposeByPitchB music21/stream/tests.py::Test::testTransposeScore music21/stream/tests.py::Test::testTwoStreamMethods music21/stream/tests.py::Test::testTwoZeroOffset music21/stream/tests.py::Test::testVoicesA music21/stream/tests.py::Test::testVoicesALonger music21/stream/tests.py::Test::testVoicesB music21/stream/tests.py::Test::testVoicesC music21/stream/tests.py::Test::testVoicesToPartsA music21/stream/tests.py::Test::testVoicesToPartsNonNoteElementPropagation music21/stream/tests.py::Test::testYieldContainers music21/stream/tests.py::Test::test_makeBeams__1_e__after_16th_note music21/stream/tests.py::Test::test_makeBeams__1_e_n_a music21/stream/tests.py::Test::test_makeBeams__all_eighths music21/stream/tests.py::Test::test_makeBeams__all_quarters music21/stream/tests.py::Test::test_makeBeams__eighth_rests_and_eighth music21/stream/tests.py::Test::test_makeBeams__paddingLeft_2_2 music21/stream/tests.py::Test::test_makeBeams__repeated_1_e_a